Merge "passpoint-r2: add the status for Trust CA certs and Provision completion"
diff --git a/Android.bp b/Android.bp
index f9b60e6..d94bd84 100644
--- a/Android.bp
+++ b/Android.bp
@@ -560,6 +560,7 @@
"telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
+ "telephony/java/com/android/internal/telephony/IRcs.aidl",
"telephony/java/com/android/internal/telephony/ISms.aidl",
"telephony/java/com/android/internal/telephony/ISub.aidl",
"telephony/java/com/android/internal/telephony/IAns.aidl",
@@ -1220,11 +1221,6 @@
srcs_lib_whitelist_dirs: frameworks_base_subdirs,
srcs_lib_whitelist_pkgs: packages_to_document,
libs: [
- "core-oj",
- "core-libart",
- "conscrypt",
- "bouncycastle",
- "okhttp",
"ext",
"framework",
"voip-common",
diff --git a/Android.mk b/Android.mk
index d333074..770ec20 100644
--- a/Android.mk
+++ b/Android.mk
@@ -87,6 +87,7 @@
frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
frameworks/base/config/hiddenapi-light-greylist.txt \
frameworks/base/config/hiddenapi-vendor-list.txt \
+ frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
frameworks/base/config/hiddenapi-force-blacklist.txt \
$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
@@ -98,6 +99,7 @@
--input-greylists \
frameworks/base/config/hiddenapi-light-greylist.txt \
frameworks/base/config/hiddenapi-vendor-list.txt \
+ frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
<(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
$(PRIVATE_GREYLIST_INPUTS) \
@@ -111,6 +113,17 @@
$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
+ frameworks/base/tools/hiddenapi/merge_csv.py \
+ $(PRIVATE_METADATA_INPUTS)
+ frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
+
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
+
# Include subdirectory makefiles
# ============================================================
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ff40f75..5c21221 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@
packages/PrintRecommendationService/
packages/PrintSpooler/
packages/PackageInstaller/
+ packages/SystemUI/
services/print/
services/usb/
telephony/
diff --git a/api/current.txt b/api/current.txt
index 9269d97..51df940 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -1199,6 +1199,7 @@
field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
+ field public static final int settingsSliceUri = 16844179; // 0x1010593
field public static final int setupActivity = 16843766; // 0x10103f6
field public static final int shadowColor = 16843105; // 0x1010161
field public static final int shadowDx = 16843106; // 0x1010162
@@ -1403,6 +1404,7 @@
field public static final int textFilterEnabled = 16843007; // 0x10100ff
field public static final int textFontWeight = 16844165; // 0x1010585
field public static final int textIsSelectable = 16843542; // 0x1010316
+ field public static final int textLocale = 16844178; // 0x1010592
field public static final int textOff = 16843045; // 0x1010125
field public static final int textOn = 16843044; // 0x1010124
field public static final int textScaleX = 16843089; // 0x1010151
@@ -5675,6 +5677,7 @@
method public java.lang.CharSequence getName();
method public android.net.Uri getSound();
method public long[] getVibrationPattern();
+ method public boolean hasUserSetImportance();
method public void setBypassDnd(boolean);
method public void setDescription(java.lang.String);
method public void setGroup(java.lang.String);
@@ -6319,6 +6322,7 @@
method public android.content.pm.ServiceInfo getServiceInfo();
method public java.lang.String getServiceName();
method public java.lang.String getSettingsActivity();
+ method public android.net.Uri getSettingsSliceUri();
method public boolean getShowMetadataInPreview();
method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
@@ -6522,6 +6526,8 @@
method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.lang.CharSequence getEndUserSessionMessage(android.content.ComponentName);
+ method public java.lang.String getGlobalPrivateDnsHost(android.content.ComponentName);
+ method public int getGlobalPrivateDnsMode(android.content.ComponentName);
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -6625,6 +6631,7 @@
method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
+ method public void setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6801,6 +6808,10 @@
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
+ field public static final int PRIVATE_DNS_MODE_OFF = 1; // 0x1
+ field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
+ field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
+ field public static final int PRIVATE_DNS_MODE_UNKNOWN = 0; // 0x0
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
@@ -9160,6 +9171,7 @@
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
method public android.net.Uri canonicalize(android.net.Uri);
+ method public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final java.lang.String getCallingPackage();
@@ -9187,6 +9199,7 @@
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
+ method public final void restoreCallingIdentity(android.content.ContentProvider.CallingIdentity);
method protected final void setPathPermissions(android.content.pm.PathPermission[]);
method protected final void setReadPermission(java.lang.String);
method protected final void setWritePermission(java.lang.String);
@@ -9195,6 +9208,9 @@
method public abstract int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
}
+ public final class ContentProvider.CallingIdentity {
+ }
+
public static abstract interface ContentProvider.PipeDataWriter<T> {
method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
}
@@ -9309,6 +9325,7 @@
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public static boolean isSyncActive(android.accounts.Account, java.lang.String);
method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
+ method public android.graphics.Bitmap loadThumbnail(android.net.Uri, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
@@ -15302,20 +15319,9 @@
method public java.nio.ByteBuffer getBuffer();
method public java.io.File getFile();
method public android.os.LocaleList getLocaleList();
+ method public int getSlant();
method public int getTtcIndex();
method public int getWeight();
- method public boolean isItalic();
- field public static final int FONT_WEIGHT_BLACK = 900; // 0x384
- field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
- field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
- field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
- field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
- field public static final int FONT_WEIGHT_MAX = 1000; // 0x3e8
- field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
- field public static final int FONT_WEIGHT_MIN = 1; // 0x1
- field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
- field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
- field public static final int FONT_WEIGHT_THIN = 100; // 0x64
}
public static class Font.Builder {
@@ -15328,7 +15334,7 @@
method public android.graphics.fonts.Font build() throws java.io.IOException;
method public android.graphics.fonts.Font.Builder setFontVariationSettings(java.lang.String);
method public android.graphics.fonts.Font.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
- method public android.graphics.fonts.Font.Builder setItalic(boolean);
+ method public android.graphics.fonts.Font.Builder setSlant(int);
method public android.graphics.fonts.Font.Builder setTtcIndex(int);
method public android.graphics.fonts.Font.Builder setWeight(int);
}
@@ -15344,6 +15350,26 @@
method public android.graphics.fonts.FontFamily build();
}
+ public final class FontStyle {
+ ctor public FontStyle();
+ ctor public FontStyle(int, int);
+ method public int getSlant();
+ method public int getWeight();
+ field public static final int FONT_SLANT_ITALIC = 1; // 0x1
+ field public static final int FONT_SLANT_UPRIGHT = 0; // 0x0
+ field public static final int FONT_WEIGHT_BLACK = 900; // 0x384
+ field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+ field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
+ field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
+ field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
+ field public static final int FONT_WEIGHT_MAX = 1000; // 0x3e8
+ field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+ field public static final int FONT_WEIGHT_MIN = 1; // 0x1
+ field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+ field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
+ field public static final int FONT_WEIGHT_THIN = 100; // 0x64
+ }
+
public final class FontVariationAxis {
ctor public FontVariationAxis(java.lang.String, float);
method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String);
@@ -15407,6 +15433,69 @@
}
+package android.graphics.text {
+
+ public class LineBreaker {
+ method public android.graphics.text.LineBreaker.Result computeLineBreaks(android.graphics.text.MeasuredText, android.graphics.text.LineBreaker.ParagraphConstraints, int);
+ field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
+ field public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; // 0x1
+ field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0
+ field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2
+ field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0
+ field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1
+ field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1
+ field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0
+ }
+
+ public static class LineBreaker.Builder {
+ ctor public LineBreaker.Builder();
+ method public android.graphics.text.LineBreaker build();
+ method public android.graphics.text.LineBreaker.Builder setBreakStrategy(int);
+ method public android.graphics.text.LineBreaker.Builder setHyphenationFrequency(int);
+ method public android.graphics.text.LineBreaker.Builder setIndents(int[]);
+ method public android.graphics.text.LineBreaker.Builder setJustified(int);
+ }
+
+ public static class LineBreaker.ParagraphConstraints {
+ ctor public LineBreaker.ParagraphConstraints();
+ method public int getDefaultTabStop();
+ method public float getFirstWidth();
+ method public int getFirstWidthLineCount();
+ method public int[] getTabStops();
+ method public float getWidth();
+ method public void setIndent(float, int);
+ method public void setTabStops(int[], int);
+ method public void setWidth(float);
+ }
+
+ public static class LineBreaker.Result {
+ method public float getLineAscent(int);
+ method public int getLineBreakOffset(int);
+ method public int getLineCount();
+ method public float getLineDescent(int);
+ method public int getLineHyphenEdit(int);
+ method public float getLineWidth(int);
+ method public boolean hasLineTab(int);
+ }
+
+ public class MeasuredText {
+ method public void getBounds(int, int, android.graphics.Rect);
+ method public float getCharWidthAt(int);
+ method public char[] getChars();
+ method public float getWidth(int, int);
+ }
+
+ public static class MeasuredText.Builder {
+ ctor public MeasuredText.Builder(char[]);
+ method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float);
+ method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean);
+ method public android.graphics.text.MeasuredText build();
+ method public android.graphics.text.MeasuredText.Builder setComputeHyphenation(boolean);
+ method public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
+ }
+
+}
+
package android.hardware {
public deprecated class Camera {
@@ -25103,6 +25192,7 @@
method public static android.net.Uri getValidRingtoneUri(android.content.Context);
method public int inferStreamType();
method public static boolean isDefault(android.net.Uri);
+ method public static android.content.res.AssetFileDescriptor openDefaultRingtoneUri(android.content.Context, android.net.Uri) throws java.io.FileNotFoundException;
method public static void setActualDefaultRingtoneUri(android.content.Context, int, android.net.Uri);
method public deprecated void setIncludeDrm(boolean);
method public void setStopPreviousRingtone(boolean);
@@ -27375,6 +27465,7 @@
public static class ConnectivityManager.NetworkCallback {
ctor public ConnectivityManager.NetworkCallback();
method public void onAvailable(android.net.Network);
+ method public void onBlockedStatusChanged(android.net.Network, boolean);
method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities);
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
@@ -27646,16 +27737,16 @@
public class NetworkInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.net.NetworkInfo.DetailedState getDetailedState();
+ method public deprecated android.net.NetworkInfo.DetailedState getDetailedState();
method public java.lang.String getExtraInfo();
method public deprecated java.lang.String getReason();
method public deprecated android.net.NetworkInfo.State getState();
- method public int getSubtype();
- method public java.lang.String getSubtypeName();
+ method public deprecated int getSubtype();
+ method public deprecated java.lang.String getSubtypeName();
method public deprecated int getType();
method public deprecated java.lang.String getTypeName();
method public deprecated boolean isAvailable();
- method public boolean isConnected();
+ method public deprecated boolean isConnected();
method public deprecated boolean isConnectedOrConnecting();
method public deprecated boolean isFailover();
method public deprecated boolean isRoaming();
@@ -29651,6 +29742,65 @@
field public static final int EGL_WINDOW_BIT = 4; // 0x4
}
+ public class EGL15 {
+ ctor public EGL15();
+ method public static int eglClientWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long);
+ method public static android.opengl.EGLSurface eglCreatePlatformPixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
+ method public static android.opengl.EGLSurface eglCreatePlatformWindowSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
+ method public static android.opengl.EGLSync eglCreateSync(android.opengl.EGLDisplay, int, long[], int);
+ method public static boolean eglDestroySync(android.opengl.EGLDisplay, android.opengl.EGLSync);
+ method public static android.opengl.EGLDisplay eglGetPlatformDisplay(int, long, long[], int);
+ method public static boolean eglGetSyncAttrib(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long[], int);
+ method public static boolean eglWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int);
+ field public static final int EGL_CL_EVENT_HANDLE = 12444; // 0x309c
+ field public static final int EGL_CONDITION_SATISFIED = 12534; // 0x30f6
+ field public static final int EGL_CONTEXT_MAJOR_VERSION = 12440; // 0x3098
+ field public static final int EGL_CONTEXT_MINOR_VERSION = 12539; // 0x30fb
+ field public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 2; // 0x2
+ field public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 1; // 0x1
+ field public static final int EGL_CONTEXT_OPENGL_DEBUG = 12720; // 0x31b0
+ field public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 12721; // 0x31b1
+ field public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 12541; // 0x30fd
+ field public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 12733; // 0x31bd
+ field public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 12722; // 0x31b2
+ field public static final long EGL_FOREVER = -1L; // 0xffffffffffffffffL
+ field public static final int EGL_GL_COLORSPACE = 12445; // 0x309d
+ field public static final int EGL_GL_COLORSPACE_LINEAR = 12426; // 0x308a
+ field public static final int EGL_GL_COLORSPACE_SRGB = 12425; // 0x3089
+ field public static final int EGL_GL_RENDERBUFFER = 12473; // 0x30b9
+ field public static final int EGL_GL_TEXTURE_2D = 12465; // 0x30b1
+ field public static final int EGL_GL_TEXTURE_3D = 12466; // 0x30b2
+ field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 12468; // 0x30b4
+ field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 12470; // 0x30b6
+ field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 12472; // 0x30b8
+ field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 12467; // 0x30b3
+ field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 12469; // 0x30b5
+ field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 12471; // 0x30b7
+ field public static final int EGL_GL_TEXTURE_LEVEL = 12476; // 0x30bc
+ field public static final int EGL_GL_TEXTURE_ZOFFSET = 12477; // 0x30bd
+ field public static final int EGL_IMAGE_PRESERVED = 12498; // 0x30d2
+ field public static final int EGL_LOSE_CONTEXT_ON_RESET = 12735; // 0x31bf
+ field public static final android.opengl.EGLContext EGL_NO_CONTEXT;
+ field public static final android.opengl.EGLDisplay EGL_NO_DISPLAY;
+ field public static final android.opengl.EGLImage EGL_NO_IMAGE;
+ field public static final int EGL_NO_RESET_NOTIFICATION = 12734; // 0x31be
+ field public static final android.opengl.EGLSurface EGL_NO_SURFACE;
+ field public static final android.opengl.EGLSync EGL_NO_SYNC;
+ field public static final int EGL_OPENGL_ES3_BIT = 64; // 0x40
+ field public static final int EGL_PLATFORM_ANDROID_KHR = 12609; // 0x3141
+ field public static final int EGL_SIGNALED = 12530; // 0x30f2
+ field public static final int EGL_SYNC_CL_EVENT = 12542; // 0x30fe
+ field public static final int EGL_SYNC_CL_EVENT_COMPLETE = 12543; // 0x30ff
+ field public static final int EGL_SYNC_CONDITION = 12536; // 0x30f8
+ field public static final int EGL_SYNC_FENCE = 12537; // 0x30f9
+ field public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 1; // 0x1
+ field public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 12528; // 0x30f0
+ field public static final int EGL_SYNC_STATUS = 12529; // 0x30f1
+ field public static final int EGL_SYNC_TYPE = 12535; // 0x30f7
+ field public static final int EGL_TIMEOUT_EXPIRED = 12533; // 0x30f5
+ field public static final int EGL_UNSIGNALED = 12531; // 0x30f3
+ }
+
public class EGLConfig extends android.opengl.EGLObjectHandle {
}
@@ -29670,6 +29820,9 @@
field public static final int EGL_RECORDABLE_ANDROID = 12610; // 0x3142
}
+ public class EGLImage extends android.opengl.EGLObjectHandle {
+ }
+
public abstract class EGLObjectHandle {
ctor protected deprecated EGLObjectHandle(int);
ctor protected EGLObjectHandle(long);
@@ -29680,6 +29833,9 @@
public class EGLSurface extends android.opengl.EGLObjectHandle {
}
+ public class EGLSync extends android.opengl.EGLObjectHandle {
+ }
+
public class ETC1 {
ctor public ETC1();
method public static void decodeBlock(java.nio.Buffer, java.nio.Buffer);
@@ -33741,6 +33897,7 @@
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_LOCATION = "no_config_location";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+ field public static final java.lang.String DISALLOW_CONFIG_PRIVATE_DNS = "disallow_config_private_dns";
field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -36673,7 +36830,7 @@
public static abstract interface MediaStore.Audio.AlbumColumns {
field public static final java.lang.String ALBUM = "album";
- field public static final java.lang.String ALBUM_ART = "album_art";
+ field public static final deprecated java.lang.String ALBUM_ART = "album_art";
field public static final java.lang.String ALBUM_ID = "album_id";
field public static final java.lang.String ALBUM_KEY = "album_key";
field public static final java.lang.String ARTIST = "artist";
@@ -36795,7 +36952,7 @@
}
public static abstract interface MediaStore.Audio.PlaylistsColumns {
- field public static final java.lang.String DATA = "_data";
+ field public static final deprecated java.lang.String DATA = "_data";
field public static final java.lang.String DATE_ADDED = "date_added";
field public static final java.lang.String DATE_MODIFIED = "date_modified";
field public static final java.lang.String NAME = "name";
@@ -36857,15 +37014,15 @@
public static class MediaStore.Images.Thumbnails implements android.provider.BaseColumns {
ctor public MediaStore.Images.Thumbnails();
- method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
- method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+ method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+ method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
method public static android.net.Uri getContentUri(java.lang.String);
- method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
- method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+ method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+ method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
method public static final android.database.Cursor queryMiniThumbnail(android.content.ContentResolver, long, int, java.lang.String[]);
method public static final android.database.Cursor queryMiniThumbnails(android.content.ContentResolver, android.net.Uri, int, java.lang.String[]);
- field public static final java.lang.String DATA = "_data";
+ field public static final deprecated java.lang.String DATA = "_data";
field public static final java.lang.String DEFAULT_SORT_ORDER = "image_id ASC";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -36880,7 +37037,7 @@
}
public static abstract interface MediaStore.MediaColumns implements android.provider.BaseColumns {
- field public static final java.lang.String DATA = "_data";
+ field public static final deprecated java.lang.String DATA = "_data";
field public static final java.lang.String DATE_ADDED = "date_added";
field public static final java.lang.String DATE_MODIFIED = "date_modified";
field public static final java.lang.String DISPLAY_NAME = "_display_name";
@@ -36908,12 +37065,12 @@
public static class MediaStore.Video.Thumbnails implements android.provider.BaseColumns {
ctor public MediaStore.Video.Thumbnails();
- method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
- method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+ method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+ method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
method public static android.net.Uri getContentUri(java.lang.String);
- method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
- method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
- field public static final java.lang.String DATA = "_data";
+ method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+ method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+ field public static final deprecated java.lang.String DATA = "_data";
field public static final java.lang.String DEFAULT_SORT_ORDER = "video_id ASC";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -42970,6 +43127,7 @@
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+ method public static int getSlotIndex(int);
method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isActiveSubscriptionId(int);
@@ -42990,6 +43148,7 @@
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+ field public static final int INVALID_SIM_SLOT_INDEX = -1; // 0xffffffff
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
}
@@ -43364,7 +43523,8 @@
package android.telephony.emergency {
- public final class EmergencyNumber implements android.os.Parcelable {
+ public final class EmergencyNumber implements java.lang.Comparable android.os.Parcelable {
+ method public int compareTo(android.telephony.emergency.EmergencyNumber);
method public int describeContents();
method public java.lang.String getCountryIso();
method public int getEmergencyNumberSourceBitmask();
@@ -44321,6 +44481,7 @@
method public static int lastIndexOf(java.lang.CharSequence, char, int);
method public static int lastIndexOf(java.lang.CharSequence, char, int, int);
method public static java.lang.CharSequence listEllipsize(android.content.Context, java.util.List<java.lang.CharSequence>, java.lang.String, android.text.TextPaint, float, int);
+ method public static java.lang.CharSequence makeSafeForPresentation(java.lang.String, int, float, int);
method public static boolean regionMatches(java.lang.CharSequence, int, java.lang.CharSequence, int, int);
method public static java.lang.CharSequence replace(java.lang.CharSequence, java.lang.String[], java.lang.CharSequence[]);
method public static java.lang.String[] split(java.lang.String, java.lang.String);
@@ -44332,6 +44493,9 @@
field public static final int CAP_MODE_SENTENCES = 16384; // 0x4000
field public static final int CAP_MODE_WORDS = 8192; // 0x2000
field public static final android.os.Parcelable.Creator<java.lang.CharSequence> CHAR_SEQUENCE_CREATOR;
+ field public static final int SAFE_STRING_FLAG_FIRST_LINE = 4; // 0x4
+ field public static final int SAFE_STRING_FLAG_SINGLE_LINE = 2; // 0x2
+ field public static final int SAFE_STRING_FLAG_TRIM = 1; // 0x1
}
public static abstract interface TextUtils.EllipsizeCallback {
@@ -44930,6 +45094,16 @@
method public abstract void drawBackground(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, int);
}
+ public static class LineBackgroundSpan.Standard implements android.text.style.LineBackgroundSpan android.text.ParcelableSpan {
+ ctor public LineBackgroundSpan.Standard(int);
+ ctor public LineBackgroundSpan.Standard(android.os.Parcel);
+ method public int describeContents();
+ method public void drawBackground(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, int);
+ method public final int getColor();
+ method public int getSpanTypeId();
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
public abstract interface LineHeightSpan implements android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
@@ -45110,6 +45284,7 @@
method public int getSpanTypeId();
method public android.content.res.ColorStateList getTextColor();
method public int getTextFontWeight();
+ method public android.os.LocaleList getTextLocales();
method public int getTextSize();
method public int getTextStyle();
method public android.graphics.Typeface getTypeface();
@@ -51261,6 +51436,7 @@
method public default android.view.textclassifier.TextClassification classifyText(android.view.textclassifier.TextClassification.Request);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default void destroy();
+ method public default android.view.textclassifier.TextLanguage detectLanguage(android.view.textclassifier.TextLanguage.Request);
method public default android.view.textclassifier.TextLinks generateLinks(android.view.textclassifier.TextLinks.Request);
method public default int getMaxGenerateLinksTextLength();
method public default boolean isDestroyed();
@@ -51301,6 +51477,39 @@
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
}
+ public final class TextLanguage implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getConfidenceScore(android.icu.util.ULocale);
+ method public android.os.Bundle getExtras();
+ method public java.lang.String getId();
+ method public android.icu.util.ULocale getLocale(int);
+ method public int getLocaleHypothesisCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage> CREATOR;
+ }
+
+ public static final class TextLanguage.Builder {
+ ctor public TextLanguage.Builder();
+ method public android.view.textclassifier.TextLanguage build();
+ method public android.view.textclassifier.TextLanguage.Builder putLocale(android.icu.util.ULocale, float);
+ method public android.view.textclassifier.TextLanguage.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.TextLanguage.Builder setId(java.lang.String);
+ }
+
+ public static final class TextLanguage.Request implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getExtras();
+ method public java.lang.CharSequence getText();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage.Request> CREATOR;
+ }
+
+ public static final class TextLanguage.Request.Builder {
+ ctor public TextLanguage.Request.Builder(java.lang.CharSequence);
+ method public android.view.textclassifier.TextLanguage.Request build();
+ method public android.view.textclassifier.TextLanguage.Request.Builder setExtras(android.os.Bundle);
+ }
+
public final class TextLinks implements android.os.Parcelable {
method public int apply(android.text.Spannable, int, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan>);
method public int describeContents();
@@ -52221,6 +52430,7 @@
field public static final int ERROR_UNSAFE_RESOURCE = -16; // 0xfffffff0
field public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3; // 0xfffffffd
field public static final int ERROR_UNSUPPORTED_SCHEME = -10; // 0xfffffff6
+ field public static final int SAFE_BROWSING_THREAT_BILLING = 4; // 0x4
field public static final int SAFE_BROWSING_THREAT_MALWARE = 1; // 0x1
field public static final int SAFE_BROWSING_THREAT_PHISHING = 2; // 0x2
field public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0; // 0x0
diff --git a/api/system-current.txt b/api/system-current.txt
index 83a789a..26036ea 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -222,7 +222,7 @@
}
public static final class R.dimen {
- field public static final int config_restricted_icon_size = 17104903; // 0x1050007
+ field public static final int config_restrictedIconSize = 17104903; // 0x1050007
}
public static final class R.drawable {
@@ -235,12 +235,12 @@
}
public static final class R.string {
- field public static final int config_feedback_intent_extra_key = 17039391; // 0x104001f
- field public static final int config_feedback_intent_name_key = 17039392; // 0x1040020
- field public static final int config_help_intent_extra_key = 17039389; // 0x104001d
- field public static final int config_help_intent_name_key = 17039390; // 0x104001e
- field public static final int config_help_package_name_key = 17039387; // 0x104001b
- field public static final int config_help_package_name_value = 17039388; // 0x104001c
+ field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
+ field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
+ field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d
+ field public static final int config_helpIntentNameKey = 17039390; // 0x104001e
+ field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
+ field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
}
public static final class R.style {
@@ -1012,7 +1012,6 @@
field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
- field public static final java.lang.String EXTRA_USER_ID = "android.intent.extra.USER_ID";
field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
}
@@ -1122,9 +1121,9 @@
method public static void forceSafeLabels();
method public deprecated java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager, float, int);
- field public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
- field public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
- field public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
+ field public static final deprecated int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
+ field public static final deprecated int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
+ field public static final deprecated int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
}
public abstract class PackageManager {
@@ -1151,7 +1150,8 @@
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
- method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
+ method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
+ method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo);
method public abstract void setUpdateAvailable(java.lang.String, boolean);
method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -1245,6 +1245,22 @@
field public int requestRes;
}
+ public final class SuspendDialogInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.SuspendDialogInfo> CREATOR;
+ }
+
+ public static final class SuspendDialogInfo.Builder {
+ ctor public SuspendDialogInfo.Builder();
+ method public android.content.pm.SuspendDialogInfo build();
+ method public android.content.pm.SuspendDialogInfo.Builder setIcon(int);
+ method public android.content.pm.SuspendDialogInfo.Builder setMessage(java.lang.String);
+ method public android.content.pm.SuspendDialogInfo.Builder setMessage(int);
+ method public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(int);
+ method public android.content.pm.SuspendDialogInfo.Builder setTitle(int);
+ }
+
}
package android.content.pm.dex {
@@ -4130,7 +4146,6 @@
public class UserManager {
method public void clearSeedAccountData();
- method public int[] getProfileIds(int, boolean);
method public java.lang.String getSeedAccountName();
method public android.os.PersistableBundle getSeedAccountOptions();
method public java.lang.String getSeedAccountType();
@@ -4456,6 +4471,23 @@
field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
}
+ public static final class Telephony.Carriers implements android.provider.BaseColumns {
+ field public static final java.lang.String APN_SET_ID = "apn_set_id";
+ field public static final int CARRIER_EDITED = 4; // 0x4
+ field public static final java.lang.String EDITED = "edited";
+ field public static final java.lang.String MAX_CONNS = "max_conns";
+ field public static final java.lang.String MAX_CONNS_TIME = "max_conns_time";
+ field public static final java.lang.String MODEM_COGNITIVE = "modem_cognitive";
+ field public static final java.lang.String MTU = "mtu";
+ field public static final int NO_SET_SET = 0; // 0x0
+ field public static final int UNEDITED = 0; // 0x0
+ field public static final int USER_DELETED = 2; // 0x2
+ field public static final java.lang.String USER_EDITABLE = "user_editable";
+ field public static final int USER_EDITED = 1; // 0x1
+ field public static final java.lang.String USER_VISIBLE = "user_visible";
+ field public static final java.lang.String WAIT_TIME = "wait_time";
+ }
+
public final class TimeZoneRulesDataContract {
field public static final java.lang.String AUTHORITY = "com.android.timezone";
}
@@ -5324,6 +5356,11 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public class PhoneStateListener {
+ method public void onRadioPowerStateChanged(int);
+ field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
+ }
+
public class ServiceState implements android.os.Parcelable {
method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
@@ -5451,6 +5488,7 @@
method public boolean getEmergencyCallbackMode();
method public java.lang.String getIsimDomain();
method public int getPreferredNetworkType(int);
+ method public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -5465,6 +5503,7 @@
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+ method public void setCarrierDataEnabled(boolean);
method public void setDataActivationState(int);
method public deprecated void setDataEnabled(int, boolean);
method public void setDataRoamingEnabled(boolean);
@@ -5514,6 +5553,9 @@
field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
+ field public static final int RADIO_POWER_OFF = 0; // 0x0
+ field public static final int RADIO_POWER_ON = 1; // 0x1
+ field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
@@ -5580,17 +5622,12 @@
}
public final class DataProfile implements android.os.Parcelable {
- ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
- ctor public DataProfile(android.os.Parcel);
- method public int describeContents();
method public java.lang.String getApn();
method public int getAuthType();
method public int getBearerBitmap();
method public int getMaxConns();
method public int getMaxConnsTime();
method public int getMtu();
- method public java.lang.String getMvnoMatchData();
- method public java.lang.String getMvnoType();
method public java.lang.String getPassword();
method public int getProfileId();
method public java.lang.String getProtocol();
@@ -5600,9 +5637,8 @@
method public java.lang.String getUserName();
method public int getWaitTime();
method public boolean isEnabled();
- method public boolean isModemCognitive();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
+ method public boolean isPersistent();
+ method public boolean isPreferred();
field public static final int TYPE_3GPP = 1; // 0x1
field public static final int TYPE_3GPP2 = 2; // 0x2
field public static final int TYPE_COMMON = 0; // 0x0
@@ -5928,11 +5964,13 @@
}
public final class ImsExternalCallState implements android.os.Parcelable {
+ ctor public ImsExternalCallState(java.lang.String, android.net.Uri, android.net.Uri, boolean, int, int, boolean);
method public int describeContents();
method public android.net.Uri getAddress();
method public int getCallId();
method public int getCallState();
method public int getCallType();
+ method public android.net.Uri getLocalAddress();
method public boolean isCallHeld();
method public boolean isCallPullable();
method public void writeToParcel(android.os.Parcel, int);
@@ -6352,7 +6390,8 @@
}
public static class MmTelFeature.MmTelCapabilities {
- ctor public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+ ctor public MmTelFeature.MmTelCapabilities();
+ ctor public deprecated MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
ctor public MmTelFeature.MmTelCapabilities(int);
method public final void addCapabilities(int);
method public final boolean isCapable(int);
@@ -6650,7 +6689,7 @@
package android.view {
public abstract class Window {
- method public void addPrivateFlags(int);
+ method public void addSystemFlags(int);
}
public abstract interface WindowManager implements android.view.ViewManager {
@@ -6660,7 +6699,10 @@
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
method public final long getUserActivityTimeout();
method public final void setUserActivityTimeout(long);
- field public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+ field public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+ }
+
+ public static abstract class WindowManager.LayoutParams.SystemFlags implements java.lang.annotation.Annotation {
}
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 2246562..4e7a114 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -91,38 +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 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 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 initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, 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 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 deprecated 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;
- }
-
- 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/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index c04e61b..41d546f 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -174,6 +174,8 @@
instrument.noWindowAnimation = true;
} else if (opt.equals("--no-hidden-api-checks")) {
instrument.disableHiddenApiChecks = true;
+ } else if (opt.equals("--no-isolated-storage")) {
+ instrument.disableIsolatedStorage = true;
} else if (opt.equals("--user")) {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 0dade0b..70baa87 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -16,6 +16,9 @@
package com.android.commands.am;
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
+
import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
import android.app.Instrumentation;
@@ -74,16 +77,13 @@
String logPath = null;
public boolean noWindowAnimation = false;
public boolean disableHiddenApiChecks = false;
+ public boolean disableIsolatedStorage = false;
public String abi = null;
public int userId = UserHandle.USER_CURRENT;
public Bundle args = new Bundle();
// Required
public String componentNameArg;
- // Disable hidden API checks for the newly started instrumentation.
- // Must be kept in sync with ActivityManagerService.
- private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
-
/**
* Construct the instrument command runner.
*/
@@ -480,7 +480,13 @@
}
// Start the instrumentation
- int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0;
+ int flags = 0;
+ if (disableHiddenApiChecks) {
+ flags |= INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+ }
+ if (disableIsolatedStorage) {
+ flags |= INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
+ }
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index db864ae..eba5586 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -33,6 +33,9 @@
LOCAL_CFLAGS += \
-Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
+
ifeq (debug,)
LOCAL_CFLAGS += \
-g -O0
@@ -100,6 +103,9 @@
LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter
+# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
+
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index f6b0db8..5818f5d 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -46,6 +46,7 @@
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
src/matchers/CombinationLogMatchingTracker.cpp \
+ src/matchers/EventMatcherWizard.cpp \
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
src/metrics/MetricProducer.cpp \
@@ -189,6 +190,7 @@
src/atom_field_options.proto \
src/atoms.proto \
src/stats_log.proto \
+ src/shell/shell_data.proto \
tests/AlarmMonitor_test.cpp \
tests/anomaly/AlarmTracker_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
@@ -216,6 +218,7 @@
tests/metrics/metrics_test_helper.cpp \
tests/statsd_test_util.cpp \
tests/e2e/WakelockDuration_e2e_test.cpp \
+ tests/e2e/MetricActivation_e2e_test.cpp \
tests/e2e/MetricConditionLink_e2e_test.cpp \
tests/e2e/Alarm_e2e_test.cpp \
tests/e2e/Attribution_e2e_test.cpp \
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 6a9e8a1..3e8b9b8 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -194,7 +194,7 @@
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
- FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+ FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
@@ -219,6 +219,7 @@
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index fb6f8c8..ce28777 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -319,7 +319,7 @@
}
if (!args[0].compare(String8("data-subscribe"))) {
if (mShellSubscriber == nullptr) {
- mShellSubscriber = new ShellSubscriber(mUidMap);
+ mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
}
mShellSubscriber->startNewSubscription(in, out, resultReceiver);
return NO_ERROR;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 957a9b4..dfb40a9 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -143,10 +143,12 @@
BatteryCausedShutdown battery_caused_shutdown = 93;
PhoneServiceStateChanged phone_service_state_changed = 94;
PhoneStateChanged phone_state_changed = 95;
+ UserRestrictionChanged user_restriction_changed = 96;
+ SettingsUIChanged settings_ui_changed = 97;
}
// Pulled events will start at field 10000.
- // Next: 10025
+ // Next: 10037
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -183,6 +185,7 @@
DiskIo disk_io = 10032;
PowerProfile power_profile = 10033;
ProcStats proc_stats_pkg_proc = 10034;
+ NativeProcessMemoryState native_process_memory_state = 10036;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -638,7 +641,7 @@
// The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock.
// From frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.WakeLockLevelEnum level = 2;
+ optional android.os.WakeLockLevelEnum type = 2;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 3;
@@ -1031,20 +1034,20 @@
// Bit mask of color capabilities of the screen.
// Contains information about the color gamut and hdr mode of the screen.
// See: https://d.android.com/reference/android/content/res/Configuration.html#colorMode
- optional int32 colorMode = 1;
+ optional int32 color_mode = 1;
// The target screen density being rendered to.
// See: https://d.android.com/reference/android/content/res/Configuration.html#densityDpi
- optional int32 densityDpi = 2;
+ optional int32 density_dpi = 2;
// Current user preference for the scaling factor for fonts,
// relative to the base density scaling.
// See: https://d.android.com/reference/android/content/res/Configuration.html#fontScale
- optional float fontScale = 3;
+ optional float font_scale = 3;
// Flag indicating whether the hard keyboard is hidden.
// See: https://d.android.com/reference/android/content/res/Configuration.html#hardKeyboardHidden
- optional int32 hardKeyboardHidden = 4;
+ optional int32 hard_keyboard_hidden = 4;
// The type of keyboard attached to the device.
// See: https://d.android.com/reference/android/content/res/Configuration.html#keyboard
@@ -1052,7 +1055,7 @@
// Flag indicating whether any keyboard is available. Takes soft keyboards into account.
// See: https://d.android.com/reference/android/content/res/Configuration.html#keyboardHidden
- optional int32 keyboardHideen = 6;
+ optional int32 keyboard_hidden = 6;
// IMSI MCC (Mobile Country Code), corresponding to mcc resource qualifier.
// 0 if undefined.
@@ -1071,7 +1074,7 @@
// Flag indicating whether the navigation is available.
// See: https://d.android.com/reference/android/content/res/Configuration.html#navigationHidden
- optional int32 navigationHidden = 10;
+ optional int32 navigation_hidden = 10;
// Overall orientation of the screen.
// See: https://d.android.com/reference/android/content/res/Configuration.html#orientation
@@ -1079,24 +1082,24 @@
// The current height of the available screen space, in dp units.
// See: https://d.android.com/reference/android/content/res/Configuration.html#screenHeightDp
- optional int32 screenHeightDp = 12;
+ optional int32 screen_height_dp = 12;
// Bit mask of overall layout of the screen.
// Contains information about screen size, whether the screen is wider/taller
// than normal, whether the screen layout is right-tl-left or left-to-right,
// and whether the screen has a rounded shape.
// See: https://d.android.com/reference/android/content/res/Configuration.html#screenLayout
- optional int32 screenLayout = 13;
+ optional int32 screen_layout = 13;
// Current width of the available screen space, in dp units.
// See: https://d.android.com/reference/android/content/res/Configuration.html#screenWidthDp
- optional int32 screenWidthDp = 14;
+ optional int32 screen_width_dp = 14;
// The smallest screen size an application will see in normal operation.
// This is the smallest value of both screenWidthDp and screenHeightDp
// in portrait and landscape.
// See: https://d.android.com/reference/android/content/res/Configuration.html#smallestScreenWidthDp
- optional int32 smallestScreenWidthDp = 15;
+ optional int32 smallest_screen_width_dp = 15;
// The type of touch screen attached to the device.
// See: https://d.android.com/reference/android/content/res/Configuration.html#touchscreen
@@ -1107,7 +1110,7 @@
// Eg: NORMAL, DESK, CAR, TELEVISION, WATCH, VR_HEADSET
// Also contains information about whether the device is in night mode.
// See: https://d.android.com/reference/android/content/res/Configuration.html#uiMode
- optional int32 uiMode = 17;
+ optional int32 ui_mode = 17;
}
@@ -1194,7 +1197,7 @@
// Eg. Airplane mode, crash, application request.
optional android.bluetooth.EnableDisableReasonEnum reason = 3;
// If the reason is an application request, this will be the package name.
- optional string pkgName = 4;
+ optional string pkg_name = 4;
}
/**
@@ -1343,7 +1346,7 @@
}
optional BatterySnapshotType type = 1;
// Temperature, in 1/10ths of degree C.
- optional int32 temperature_deci_celcius = 2;
+ optional int32 temperature_deci_celsius = 2;
// Voltage Battery Voltage, in microVolts.
optional int32 voltage_micro_volt = 3;
// Current Battery current, in microAmps.
@@ -1435,6 +1438,61 @@
}
/**
+ * Logs when Settings UI has changed.
+ *
+ * Logged from:
+ * packages/apps/Settings
+ */
+message SettingsUIChanged {
+ /**
+ * The action performed in this event
+ */
+ enum Action {
+ ACTION_UNKNOWN = 0;
+ PAGE_VISIBLE = 1;
+ PAGE_HIDE = 2;
+ PREF_CHANGE = 3;
+ }
+
+ /**
+ * Id for Settings pages. Each page must have its own unique Id.
+ */
+ enum PageId {
+ // Unknown page. Should not be used in production code.
+ PAGE_UNKNOWN = 0;
+
+ // Settings > Display > Lock screen display > On lock screen
+ LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
+ }
+
+ /**
+ * Where this SettingsUIChange event comes from. For example, if
+ * it's a PAGE_VISIBLE event, where the page is opened from.
+ */
+ optional PageId attribution = 1;
+
+ /**
+ * What the UI action is.
+ */
+ optional Action action = 2;
+
+ /**
+ * Where the action is happening
+ */
+ optional PageId pageId = 3;
+
+ /**
+ * What preference changed in this event.
+ */
+ optional string changedPreferenceKey = 4;
+
+ /**
+ * The new value of the changed preference.
+ */
+ optional int64 changedPreferenceIntValue = 5;
+}
+
+/**
* Logs that a setting was updated.
* Logged from:
* frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -1871,10 +1929,10 @@
optional string activity_name = 3;
// # of page-faults
- optional int64 pgfault = 4;
+ optional int64 page_fault = 4;
// # of major page-faults
- optional int64 pgmajfault = 5;
+ optional int64 page_major_fault = 5;
// RSS
optional int64 rss_in_bytes = 6;
@@ -1914,13 +1972,13 @@
optional string process_name = 2;
// oom adj score.
- optional int32 oom_score = 3;
+ optional int32 oom_adj_score = 3;
// # of page-faults
- optional int64 pgfault = 4;
+ optional int64 page_fault = 4;
// # of major page-faults
- optional int64 pgmajfault = 5;
+ optional int64 page_major_fault = 5;
// RSS
optional int64 rss_in_bytes = 6;
@@ -2182,7 +2240,7 @@
optional int32 version = 3;
- optional int64 time = 4;
+ optional int64 time_micros = 4;
}
/**
@@ -2246,11 +2304,11 @@
// stack reported state
// TODO: replace this with proto enum
optional int32 stack_state = 2;
- // tx time in ms
+ // tx time in millis
optional uint64 controller_tx_time_millis = 3;
- // rx time in ms
+ // rx time in millis
optional uint64 controller_rx_time_millis = 4;
- // idle time in ms
+ // idle time in millis
optional uint64 controller_idle_time_millis = 5;
// product of current(mA), voltage(V) and time(ms)
optional uint64 controller_energy_used = 6;
@@ -2262,9 +2320,9 @@
message ModemActivityInfo {
// timestamp(wall clock) of record creation
optional uint64 timestamp_millis = 1;
- // sleep time in ms.
+ // sleep time in millis.
optional uint64 sleep_time_millis = 2;
- // idle time in ms
+ // idle time in millis
optional uint64 controller_idle_time_millis = 3;
/**
* Tx power index
@@ -2299,11 +2357,11 @@
optional uint64 timestamp_millis = 1;
// bluetooth stack state
optional int32 bluetooth_stack_state = 2;
- // tx time in ms
+ // tx time in millis
optional uint64 controller_tx_time_millis = 3;
- // rx time in ms
+ // rx time in millis
optional uint64 controller_rx_time_millis = 4;
- // idle time in ms
+ // idle time in millis
optional uint64 controller_idle_time_millis = 5;
// product of current(mA), voltage(V) and time(ms)
optional uint64 energy_used = 6;
@@ -2320,13 +2378,13 @@
optional string process_name = 2;
// oom adj score.
- optional int32 oom_score = 3;
+ optional int32 oom_adj_score = 3;
// # of page-faults
- optional int64 pgfault = 4;
+ optional int64 page_fault = 4;
// # of major page-faults
- optional int64 pgmajfault = 5;
+ optional int64 page_major_fault = 5;
// RSS
optional int64 rss_in_bytes = 6;
@@ -2344,6 +2402,31 @@
}
/*
+ * Logs the memory stats for a native process (from procfs).
+ */
+message NativeProcessMemoryState {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process name.
+ optional string process_name = 2;
+
+ // # of page-faults
+ optional int64 page_fault = 3;
+
+ // # of major page-faults
+ optional int64 page_major_fault = 4;
+
+ // RSS
+ optional int64 rss_in_bytes = 5;
+
+ // RSS high watermark.
+ // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
+ // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
+ optional int64 rss_high_watermark_in_bytes = 6;
+}
+
+/*
* Elapsed real time from SystemClock.
*/
message SystemElapsedRealtime {
@@ -2408,7 +2491,7 @@
* frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
*/
message RemainingBatteryCapacity {
- optional int32 charge_uAh = 1;
+ optional int32 charge_micro_ampere_hour = 1;
}
/**
@@ -2417,7 +2500,7 @@
* frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
*/
message FullBatteryCapacity {
- optional int32 capacity_uAh = 1;
+ optional int32 capacity_micro_ampere_hour = 1;
}
/**
@@ -2427,7 +2510,7 @@
*/
message BatteryVoltage {
// The voltage of the battery, in millivolts.
- optional int32 voltage_mV = 1;
+ optional int32 voltage_millivolt = 1;
}
/**
@@ -2445,7 +2528,7 @@
optional string sensor_name = 2;
// Temperature in tenths of a degree C.
- optional int32 temperature_dC = 3;
+ optional int32 temperature_deci_celsius = 3;
}
/**
@@ -2555,8 +2638,7 @@
// recorded_message_count.
//
// If recorded_message_count is different than message_count, it means data
- // collection has been sampled. All the fields below will be sampled in this
- // case.
+ // collection has been sampled. The fields below will be sampled in this case.
optional int64 recorded_message_count = 7;
// Total latency of all processed messages.
@@ -2572,6 +2654,32 @@
// True if the screen was interactive PowerManager#isInteractive at the end of the call.
optional bool screen_interactive = 10;
+
+ // Max recorded CPU usage of all processed messages.
+ optional int64 recorded_max_cpu_micros = 11;
+
+ // Max recorded latency of all processed messages.
+ optional int64 recorded_max_latency_micros = 12;
+
+ // Total number of messages we tracked the dispatching delay for. If we
+ // collected data for all the messages, message_count will be equal to
+ // recorded_delay_message_count.
+ //
+ // If recorded_delay_message_count is different than message_count, it means data
+ // collection has been sampled or/and not all messages specified the target dispatch time.
+ // The fields below will be sampled in this case.
+ optional int64 recorded_delay_message_count = 13;
+
+ // Total dispatching delay of all processed messages.
+ // Calculated as a difference between the target dispatching time (Message.when)
+ // and the actual dispatching time.
+ // Average can be computed using recorded_total_delay_millis / recorded_delay_message_count.
+ optional int64 recorded_total_delay_millis = 14;
+
+ // Max dispatching delay of all processed messages.
+ // Calculated as a difference between the target dispatching time (Message.when)
+ // and the actual dispatching time.
+ optional int64 recorded_max_delay_millis = 15;
}
/**
@@ -2708,10 +2816,10 @@
optional android.service.procstats.ProcessState process_state = 3;
// Millisecond uptime duration spent in this state
- optional int64 duration_ms = 4;
+ optional int64 duration_millis = 4;
// Millisecond elapsed realtime duration spent in this state
- optional int64 realtime_duration_ms = 9;
+ optional int64 realtime_duration_millis = 9;
// # of samples taken
optional int32 sample_size = 5;
@@ -2770,9 +2878,9 @@
optional android.service.procstats.MemoryState memory_state = 2;
// duration in milliseconds.
- optional int64 duration_ms = 3;
+ optional int64 duration_millis = 3;
// Millisecond elapsed realtime duration spent in this state
- optional int64 realtime_duration_ms = 4;
+ optional int64 realtime_duration_millis = 4;
}
repeated StateStats state_stats = 3;
}
@@ -2796,7 +2904,7 @@
optional int32 total_count = 3;
// Millisecond uptime total duration this association was around.
- optional int64 total_duration_ms = 4;
+ optional int64 total_duration_millis = 4;
// Total count of the times this association became actively impacting its target process.
optional int32 active_count = 5;
@@ -2806,9 +2914,9 @@
// Process state enum.
optional android.service.procstats.ProcessState process_state = 1;
// Millisecond uptime duration spent in this state
- optional int64 duration_ms = 2;
+ optional int64 duration_millis = 2;
// Millisecond elapsed realtime duration spent in this state
- optional int64 realtime_duration_ms = 3;
+ optional int64 realtime_duration_mmillis = 3;
}
repeated StateStats active_state_stats = 6;
}
@@ -2843,16 +2951,16 @@
message ProcessStatsSectionProto {
// Elapsed realtime at start of report.
- optional int64 start_realtime_ms = 1;
+ optional int64 start_realtime_millis = 1;
// Elapsed realtime at end of report.
- optional int64 end_realtime_ms = 2;
+ optional int64 end_realtime_millis = 2;
// CPU uptime at start of report.
- optional int64 start_uptime_ms = 3;
+ optional int64 start_uptime_millis = 3;
// CPU uptime at end of report.
- optional int64 end_uptime_ms = 4;
+ optional int64 end_uptime_millis = 4;
// System runtime library. e.g. "libdvm.so", "libart.so".
optional string runtime = 5;
@@ -2981,3 +3089,17 @@
message PowerProfile {
optional PowerProfileProto power_profile = 1;
}
+
+/**
+ * Logs when a user restriction was added or removed.
+ *
+ * Logged from:
+ * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ */
+message UserRestrictionChanged {
+ // The raw string of the user restriction as defined in UserManager.
+ // Allowed values are defined in UserRestrictionsUtils#USER_RESTRICTIONS.
+ optional string restriction = 1;
+ // Whether the restriction is enabled or disabled.
+ optional bool enabled = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index dab64ca..1a9ba8a 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -170,6 +170,12 @@
{2, 3},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+ // native_process_memory_state
+ {android::util::NATIVE_PROCESS_MEMORY_STATE,
+ {{3, 4, 5, 6},
+ {2},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
// temperature
{android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
// binder_calls
diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.cpp b/cmds/statsd/src/matchers/EventMatcherWizard.cpp
new file mode 100644
index 0000000..8418e98
--- /dev/null
+++ b/cmds/statsd/src/matchers/EventMatcherWizard.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "EventMatcherWizard.h"
+#include <unordered_set>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+using std::string;
+using std::vector;
+
+MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) {
+ if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) {
+ return MatchingState::kNotComputed;
+ }
+ vector<MatchingState> matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed);
+ mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache);
+ return matcherCache[matcher_index];
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h
new file mode 100644
index 0000000..57ec2b3
--- /dev/null
+++ b/cmds/statsd/src/matchers/EventMatcherWizard.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "LogMatchingTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class EventMatcherWizard : public virtual android::RefBase {
+public:
+ EventMatcherWizard(){}; // for testing
+ EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers)
+ : mAllEventMatchers(eventTrackers){};
+
+ virtual ~EventMatcherWizard(){};
+
+ MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
+
+private:
+ std::vector<sp<LogMatchingTracker>> mAllEventMatchers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 02b9773..f5a16e9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -69,11 +69,16 @@
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId,
+ const sp<ConditionWizard>& wizard,
+ const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const int pullTagId,
const int triggerAtomId, const int atomId,
const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+ mWhatMatcherIndex(whatMatcherIndex),
+ mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
mPullTagId(pullTagId),
mTriggerAtomId(triggerAtomId),
@@ -136,7 +141,7 @@
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
if (mIsPulled) {
- pullLocked(startTimeNs);
+ pullAndMatchEventsLocked(startTimeNs);
}
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -302,7 +307,7 @@
mPastBuckets.clear();
}
-void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
+void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
bool triggerPuller = false;
switch(mSamplingType) {
// When the metric wants to do random sampling and there is already one gauge atom for the
@@ -331,7 +336,10 @@
return;
}
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data);
+ if (mEventMatcherWizard->matchLogEvent(
+ *data, mWhatMatcherIndex) == MatchingState::kMatched) {
+ onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ }
}
}
@@ -341,7 +349,7 @@
flushIfNeededLocked(eventTimeNs);
mCondition = conditionMet;
if (mIsPulled) {
- pullLocked(eventTimeNs);
+ pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -354,7 +362,7 @@
// pull for every dimension.
mCondition = overallCondition;
if (mIsPulled) {
- pullLocked(eventTimeNs);
+ pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -387,7 +395,10 @@
return;
}
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data);
+ if (mEventMatcherWizard->matchLogEvent(
+ *data, mWhatMatcherIndex) == MatchingState::kMatched) {
+ onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ }
}
}
@@ -426,7 +437,7 @@
flushIfNeededLocked(eventTimeNs);
if (mTriggerAtomId == event.GetTagId()) {
- pullLocked(eventTimeNs);
+ pullAndMatchEventsLocked(eventTimeNs);
return;
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6379389..99827bb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -24,6 +24,7 @@
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
#include "../matchers/matcher_util.h"
+#include "../matchers/EventMatcherWizard.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "../stats_util.h"
@@ -56,7 +57,9 @@
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
+ const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
+ const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard,
const int pullTagId, const int triggerAtomId, const int atomId,
const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager);
@@ -78,7 +81,7 @@
flushCurrentBucketLocked(eventTimeNs);
mCurrentBucketStartTimeNs = eventTimeNs;
if (mIsPulled) {
- pullLocked(eventTimeNs);
+ pullAndMatchEventsLocked(eventTimeNs);
}
};
@@ -113,7 +116,11 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
- void pullLocked(const int64_t timestampNs);
+ void pullAndMatchEventsLocked(const int64_t timestampNs);
+
+ const int mWhatMatcherIndex;
+
+ sp<EventMatcherWizard> mEventMatcherWizard;
sp<StatsPullerManager> mPullerManager;
// tagId for pulled data. -1 if this is not pulled
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index df08181..f87849e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -64,8 +64,54 @@
onMatchedLogEventInternalLocked(
matcherIndex, metricKey, conditionKey, condition, event);
}
+}
- }
+bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
+ bool isActive = mEventActivationMap.empty();
+ for (auto& it : mEventActivationMap) {
+ if (it.second.state == ActivationState::kActive &&
+ elapsedTimestampNs > it.second.ttl_ns + it.second.activation_ns) {
+ it.second.state = ActivationState::kNotActive;
+ }
+ if (it.second.state == ActivationState::kActive) {
+ isActive = true;
+ }
+ }
+ return isActive;
+}
+
+void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mIsActive) {
+ return;
+ }
+ mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
+ if (!mIsActive) {
+ flushLocked(elapsedTimestampNs);
+ }
+}
+
+void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ // When a metric producer does not depend on any activation, its mIsActive is true.
+ // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
+ // change.
+ if (mEventActivationMap.empty()) {
+ mIsActive = false;
+ }
+ mEventActivationMap[activationTrackerIndex].ttl_ns = ttl_seconds * NS_PER_SEC;
+}
+
+void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
+ auto it = mEventActivationMap.find(activationTrackerIndex);
+ if (it == mEventActivationMap.end()) {
+ return;
+ }
+ it->second.activation_ns = elapsedTimestampNs;
+ it->second.state = ActivationState::kActive;
+ mIsActive = true;
+}
+
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 6fe4bfb..b21fd50 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -34,6 +34,17 @@
namespace os {
namespace statsd {
+// If the metric has no activation requirement, it will be active once the metric producer is
+// created.
+// If the metric needs to be activated by atoms, the metric producer will start
+// with kNotActive state, turn to kActive when the activation event arrives, become kNotActive
+// when it reaches the duration limit (timebomb). If the activation event arrives again before
+// or after it expires, the event producer will be re-activated and ttl will be reset.
+enum ActivationState {
+ kNotActive = 0,
+ kActive = 1,
+};
+
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
@@ -54,7 +65,8 @@
mContainANYPositionInDimensionsInWhat(false),
mSliceByPositionALL(false),
mSameConditionDimensionsInTracker(false),
- mHasLinksToAllConditionDimensionsInTracker(false) {
+ mHasLinksToAllConditionDimensionsInTracker(false),
+ mIsActive(true) {
}
virtual ~MetricProducer(){};
@@ -93,17 +105,23 @@
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
- onMatchedLogEventLocked(matcherIndex, event);
+ if (mIsActive) {
+ onMatchedLogEventLocked(matcherIndex, event);
+ }
}
void onConditionChanged(const bool condition, const int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
- onConditionChangedLocked(condition, eventTime);
+ if (mIsActive) {
+ onConditionChangedLocked(condition, eventTime);
+ }
}
void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
- onSlicedConditionMayChangeLocked(overallCondition, eventTime);
+ if (mIsActive) {
+ onSlicedConditionMayChangeLocked(overallCondition, eventTime);
+ }
}
bool isConditionSliced() const {
@@ -177,6 +195,15 @@
return mCurrentBucketNum;
}
+ void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ activateLocked(activationTrackerIndex, elapsedTimestampNs);
+ }
+
+ void addActivation(int activationTrackerIndex, int64_t ttl_seconds);
+
+ void flushIfExpire(int64_t elapsedTimestampNs);
+
protected:
virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -189,6 +216,10 @@
virtual size_t byteSizeLocked() const = 0;
virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
+ bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
+
+ void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time. This will
also flush the current partial bucket in memory.
@@ -198,9 +229,9 @@
/**
* Flushes all the data including the current partial bucket.
*/
- virtual void flushLocked(const int64_t& eventTime) {
- flushIfNeededLocked(eventTime);
- flushCurrentBucketLocked(eventTime);
+ virtual void flushLocked(const int64_t& eventTimeNs) {
+ flushIfNeededLocked(eventTimeNs);
+ flushCurrentBucketLocked(eventTimeNs);
};
/**
@@ -295,6 +326,21 @@
virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
mutable std::mutex mMutex;
+
+ struct Activation {
+ Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive) {}
+
+ int64_t ttl_ns;
+ int64_t activation_ns;
+ ActivationState state;
+ };
+ // When the metric producer has multiple activations, these activations are ORed to determine
+ // whether the metric producer is ready to generate metrics.
+ std::unordered_map<int, Activation> mEventActivationMap;
+
+ bool mIsActive;
+
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 0e5ef4d..f85ba1f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -73,7 +73,8 @@
key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
- mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
+ mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
+ mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
@@ -298,7 +299,12 @@
}
int tagId = event.GetTagId();
- int64_t eventTime = event.GetElapsedTimestampNs();
+ int64_t eventTimeNs = event.GetElapsedTimestampNs();
+
+ for (int metric : mMetricIndexesWithActivation) {
+ mAllMetricProducers[metric]->flushIfExpire(eventTimeNs);
+ }
+
if (mTagIds.find(tagId) == mTagIds.end()) {
// not interesting...
return;
@@ -310,6 +316,14 @@
matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
}
+ for (const auto& it : mActivationAtomTrackerToMetricMap) {
+ if (matcherCache[it.first] == MatchingState::kMatched) {
+ for (int metricIndex : it.second) {
+ mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
+ }
+ }
+ }
+
// A bitmap to see which ConditionTracker needs to be re-evaluated.
vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
@@ -347,13 +361,13 @@
// Push the new condition to it directly.
if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
- eventTime);
+ eventTimeNs);
// metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
} else {
mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
- eventTime);
+ eventTimeNs);
}
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index dfbb69f..649222ff 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -195,6 +195,11 @@
// maps from ConditionTracker to MetricProducer
std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
+ // maps from life span triggering event to MetricProducers.
+ std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap;
+
+ std::vector<int> mMetricIndexesWithActivation;
+
void initLogSourceWhiteList();
// The metrics that don't need to be uploaded or even reported.
@@ -207,7 +212,7 @@
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
- FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+ FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
@@ -230,6 +235,7 @@
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 75d6df9..136ba07 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -25,6 +25,7 @@
#include "../external/StatsPullerManager.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
+#include "../matchers/EventMatcherWizard.h"
#include "../metrics/CountMetricProducer.h"
#include "../metrics/DurationMetricProducer.h"
#include "../metrics/EventMetricProducer.h"
@@ -294,6 +295,7 @@
unordered_map<int, std::vector<int>>& trackerToMetricMap,
unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+ sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
config.event_metric_size() + config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
@@ -563,7 +565,8 @@
}
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, wizard, pullTagId, triggerAtomId, atomTagId,
+ key, metric, conditionIndex, wizard,
+ trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
timeBaseTimeNs, currentTimeNs, pullerManager);
allMetricProducers.push_back(gaugeProducer);
}
@@ -682,6 +685,44 @@
return true;
}
+bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config,
+ const int64_t currentTimeNs,
+ const unordered_map<int64_t, int> &logEventTrackerMap,
+ const unordered_map<int64_t, int> &metricProducerMap,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ for (int i = 0; i < config.metric_activation_size(); ++i) {
+ const MetricActivation& metric_activation = config.metric_activation(i);
+ auto itr = metricProducerMap.find(metric_activation.metric_id());
+ if (itr == metricProducerMap.end()) {
+ ALOGE("Metric id not found in metric activation: %lld",
+ (long long)metric_activation.metric_id());
+ return false;
+ }
+ const int metricTrackerIndex = itr->second;
+ if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) {
+ ALOGE("Invalid metric tracker index.");
+ return false;
+ }
+ metricsWithActivation.push_back(metricTrackerIndex);
+ for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
+ const EventActivation& activation = metric_activation.event_activation(j);
+ auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id());
+ if (logTrackerIt == logEventTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event activation.");
+ return false;
+ }
+ const int atomMatcherIndex = logTrackerIt->second;
+ activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
+ metricTrackerIndex);
+ allMetricProducers[metricTrackerIndex]->addActivation(
+ atomMatcherIndex, activation.ttl_seconds());
+ }
+ }
+ return true;
+}
+
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -695,6 +736,8 @@
unordered_map<int, std::vector<int>>& conditionToMetricMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds) {
unordered_map<int64_t, int> logTrackerMap;
unordered_map<int64_t, int> conditionTrackerMap;
@@ -729,6 +772,11 @@
ALOGE("initAlarms failed");
return false;
}
+ if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
+ allMetricProducers, activationAtomTrackerToMetricMap, metricsWithActivation)) {
+ ALOGE("initMetricActivations failed");
+ return false;
+ }
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index c660149..9ffceda 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -108,6 +108,8 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ unordered_map<int, std::vector<int>>& lifeSpanEventTrackerToMetricMap,
+ vector<int>& metricsWithLifeSpan,
std::set<int64_t>& noReportMetricIds);
bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 1306a46..dffff7a 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -18,9 +18,9 @@
#include "ShellSubscriber.h"
-#include "matchers/matcher_util.h"
-
#include <android-base/file.h>
+#include "matchers/matcher_util.h"
+#include "stats_log_util.h"
using android::util::ProtoOutputStream;
@@ -28,6 +28,8 @@
namespace os {
namespace statsd {
+const static int FIELD_ID_ATOM = 1;
+
void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) {
VLOG("start new shell subscription");
{
@@ -42,25 +44,106 @@
IInterface::asBinder(mResultReceiver)->linkToDeath(this);
}
- // Spawn another thread to read the config updates from the input file descriptor
- std::thread reader([in, this] { readConfig(in); });
- reader.detach();
+ // Note that the following is blocking, and it's intended as we cannot return until the shell
+ // cmd exits, otherwise all resources & FDs will be automatically closed.
+ // Read config forever until EOF is reached. Clients may send multiple configs -- each new
+ // config replace the previous one.
+ readConfig(in);
+
+ // Now we have read an EOF we now wait for the semaphore until the client exits.
+ VLOG("Now wait for client to exit");
std::unique_lock<std::mutex> lk(mMutex);
-
mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
- if (reader.joinable()) {
- reader.join();
- }
}
void ShellSubscriber::updateConfig(const ShellSubscription& config) {
std::lock_guard<std::mutex> lock(mMutex);
mPushedMatchers.clear();
+ mPulledInfo.clear();
+
for (const auto& pushed : config.pushed()) {
mPushedMatchers.push_back(pushed);
VLOG("adding matcher for atom %d", pushed.atom_id());
}
+
+ int64_t token = getElapsedRealtimeNs();
+ mPullToken = token;
+
+ int64_t minInterval = -1;
+ for (const auto& pulled : config.pulled()) {
+ // All intervals need to be multiples of the min interval.
+ if (minInterval < 0 || pulled.freq_millis() < minInterval) {
+ minInterval = pulled.freq_millis();
+ }
+
+ mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis());
+ VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
+ }
+
+ if (mPulledInfo.size() > 0 && minInterval > 0) {
+ // This thread is guaranteed to terminate after it detects the token is different or
+ // cleaned up.
+ std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
+ puller.detach();
+ }
+}
+
+void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ const SimpleAtomMatcher& matcher) {
+ if (mOutput == 0) return;
+ int count = 0;
+ mProto.clear();
+ for (const auto& event : data) {
+ VLOG("%s", event->ToString().c_str());
+ if (matchesSimple(*mUidMap, matcher, *event)) {
+ VLOG("matched");
+ count++;
+ uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+ util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+ event->ToProto(mProto);
+ mProto.end(atomToken);
+ }
+ }
+
+ if (count > 0) {
+ // First write the payload size.
+ size_t bufferSize = mProto.size();
+ write(mOutput, &bufferSize, sizeof(bufferSize));
+ VLOG("%d atoms, proto size: %zu", count, bufferSize);
+ // Then write the payload.
+ mProto.flush(mOutput);
+ }
+ mProto.clear();
+}
+
+void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
+ while (1) {
+ int64_t nowMillis = getElapsedRealtimeMillis();
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mPulledInfo.size() == 0 || mPullToken != token) {
+ VLOG("Pulling thread %lld done!", (long long)token);
+ return;
+ }
+ for (auto& pullInfo : mPulledInfo) {
+ if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval < nowMillis) {
+ VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
+
+ vector<std::shared_ptr<LogEvent>> data;
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), nowMillis * 1000000L,
+ &data);
+ VLOG("pulled %zu atoms", data.size());
+ if (data.size() > 0) {
+ writeToOutputLocked(data, pullInfo.mPullerMatcher);
+ }
+ pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
+ }
+ }
+ }
+ VLOG("Pulling thread %lld sleep....", (long long)token);
+ std::this_thread::sleep_for(std::chrono::milliseconds(intervalMillis));
+ }
}
void ShellSubscriber::readConfig(int in) {
@@ -101,6 +184,8 @@
mOutput = 0;
mResultReceiver = nullptr;
mPushedMatchers.clear();
+ mPulledInfo.clear();
+ mPullToken = 0;
VLOG("done clean up");
}
@@ -110,10 +195,13 @@
if (mOutput <= 0) {
return;
}
-
for (const auto& matcher : mPushedMatchers) {
if (matchesSimple(*mUidMap, matcher, event)) {
+ VLOG("%s", event.ToString().c_str());
+ uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+ util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
event.ToProto(mProto);
+ mProto.end(atomToken);
// First write the payload size.
size_t bufferSize = mProto.size();
write(mOutput, &bufferSize, sizeof(bufferSize));
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 0ace35f..5401f31 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -24,6 +24,7 @@
#include <mutex>
#include <string>
#include <thread>
+#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "packages/UidMap.h"
@@ -51,14 +52,15 @@
* with sizeof(size_t) bytes indicating the size of the proto message payload.
*
* The stream would be in the following format:
- * |size_t|atom1 proto|size_t|atom2 proto|....
+ * |size_t|shellData proto|size_t|shellData proto|....
*
* Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
* until it exits.
*/
class ShellSubscriber : public virtual IBinder::DeathRecipient {
public:
- ShellSubscriber(sp<UidMap> uidMap) : mUidMap(uidMap){};
+ ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
+ : mUidMap(uidMap), mPullerMgr(pullerMgr){};
/**
* Start a new subscription.
@@ -70,15 +72,28 @@
void onLogEvent(const LogEvent& event);
private:
+ struct PullInfo {
+ PullInfo(const SimpleAtomMatcher& matcher, int64_t interval)
+ : mPullerMatcher(matcher), mInterval(interval), mPrevPullElapsedRealtimeMs(0) {
+ }
+ SimpleAtomMatcher mPullerMatcher;
+ int64_t mInterval;
+ int64_t mPrevPullElapsedRealtimeMs;
+ };
void readConfig(int in);
void updateConfig(const ShellSubscription& config);
+ void startPull(int64_t token, int64_t intervalMillis);
+
void cleanUpLocked();
+ void writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ const SimpleAtomMatcher& matcher);
+
sp<UidMap> mUidMap;
- // bool mWritten = false;
+ sp<StatsPullerManager> mPullerMgr;
android::util::ProtoOutputStream mProto;
@@ -93,6 +108,10 @@
sp<IResultReceiver> mResultReceiver;
std::vector<SimpleAtomMatcher> mPushedMatchers;
+
+ std::vector<PullInfo> mPulledInfo;
+
+ int64_t mPullToken = 0; // A unique token to identify a puller thread.
};
} // namespace statsd
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
index 516693d..73cb49a 100644
--- a/cmds/statsd/src/shell/shell_config.proto
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -24,7 +24,7 @@
import "frameworks/base/cmds/statsd/src/statsd_config.proto";
message PulledAtomSubscription {
- optional int32 atom_id = 1;
+ optional SimpleAtomMatcher matcher = 1;
/* gap between two pulls in milliseconds */
optional int32 freq_millis = 2;
diff --git a/cmds/statsd/src/shell/shell_data.proto b/cmds/statsd/src/shell/shell_data.proto
new file mode 100644
index 0000000..236bdbd
--- /dev/null
+++ b/cmds/statsd/src/shell/shell_data.proto
@@ -0,0 +1,29 @@
+/*
+ * 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";
+
+package android.os.statsd;
+
+option java_package = "com.android.os.statsd";
+option java_outer_classname = "ShellDataProto";
+
+import "frameworks/base/cmds/statsd/src/atoms.proto";
+
+// The output of shell subscription, including both pulled and pushed subscriptions.
+message ShellData {
+ repeated Atom atom = 1;
+}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index d19e247..d5f81a59 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -347,6 +347,17 @@
optional float probability_of_informing = 7 [default = 1.1];
}
+message EventActivation {
+ optional int64 atom_matcher_id = 1;
+ optional int64 ttl_seconds = 2;
+}
+
+message MetricActivation {
+ optional int64 metric_id = 1;
+
+ repeated EventActivation event_activation = 2;
+}
+
message StatsdConfig {
optional int64 id = 1;
@@ -384,6 +395,8 @@
optional bool hash_strings_in_metric_report = 16 [default = true];
+ repeated MetricActivation metric_activation = 17;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 8fbb58a..f8184d8 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -282,13 +282,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
EXPECT_EQ(1u, allMetricProducers.size());
EXPECT_EQ(1u, allAnomalyTrackers.size());
EXPECT_EQ(1u, noReportMetricIds.size());
@@ -309,13 +313,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -333,13 +341,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -357,12 +369,16 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -380,12 +396,16 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -403,13 +423,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -427,13 +451,17 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
+ unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+ vector<int> metricsWithLifeSpan;
std::set<int64_t> noReportMetricIds;
EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
allAtomMatchers, allConditionTrackers, allMetricProducers,
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+ trackerToMetricMap, trackerToConditionMap,
+ lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+ noReportMetricIds));
}
#else
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 5b6f167..d7b9c11 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -152,7 +152,7 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1,
@@ -161,7 +161,7 @@
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(2).atom_size());
EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -170,7 +170,7 @@
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(3).atom_size());
EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
@@ -179,7 +179,7 @@
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(3).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(3).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(3).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(4).atom_size());
EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
@@ -188,7 +188,7 @@
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(4).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(4).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(4).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(5).atom_size());
EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
@@ -197,11 +197,11 @@
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(5).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_deci_celsius(), 0);
}
-TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) {
- auto config = CreateStatsdConfig(GaugeMetric::ALL_CONDITION_CHANGES);
+TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
+ auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
int64_t baseTimeNs = 10 * NS_PER_SEC;
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
int64_t bucketSizeNs =
@@ -275,7 +275,7 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100,
@@ -284,7 +284,7 @@
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(2, data.bucket_info(2).atom_size());
EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -295,9 +295,9 @@
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_TRUE(data.bucket_info(2).atom(1).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(1).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(2).atom(1).temperature().temperature_deci_celsius(), 0);
}
@@ -378,7 +378,7 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
@@ -387,7 +387,7 @@
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_deci_celsius(), 0);
EXPECT_EQ(1, data.bucket_info(2).atom_size());
EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -396,7 +396,7 @@
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+ EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_deci_celsius(), 0);
}
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
new file mode 100644
index 0000000..0f13a4a
--- /dev/null
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -0,0 +1,242 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto crashMatcher = CreateProcessCrashAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
+
+ *config.add_atom_matcher() = saverModeMatcher;
+ *config.add_atom_matcher() = crashMatcher;
+ *config.add_atom_matcher() = screenOnMatcher;
+
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(crashMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ countMetric->mutable_dimensions_in_what()->set_field(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId);
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
+ auto event_activation2 = metric_activation1->add_event_activation();
+ event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
+
+ return config;
+}
+
+} // namespace
+
+TEST(MetricActivationE2eTest, TestCountMetric) {
+ auto config = CreateStatsdConfig();
+
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ sp<MetricProducer> metricProducer =
+ processor->mMetricsManagers.begin()->second->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+
+ EXPECT_FALSE(metricProducer->mIsActive);
+ // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+ // triggered by screen on event (tracker index 2).
+ EXPECT_EQ(eventActivationMap.size(), 2u);
+ EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+ EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0].activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2].activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+ std::unique_ptr<LogEvent> event;
+
+ event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
+ processor->OnLogEvent(event.get());
+ EXPECT_FALSE(metricProducer->mIsActive);
+
+ // Activated by battery save mode.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+ processor->OnLogEvent(event.get());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2].activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+ // First processed event.
+ event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
+ processor->OnLogEvent(event.get());
+
+ // Activated by screen on event.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + 20);
+ processor->OnLogEvent(event.get());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+ // 2nd processed event.
+ // The activation by screen_on event expires, but the one by battery save mode is still active.
+ event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+ processor->OnLogEvent(event.get());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+ // 3rd processed event.
+ event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+ processor->OnLogEvent(event.get());
+
+ // All activations expired.
+ event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+ processor->OnLogEvent(event.get());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+ // Re-activate.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+ bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ processor->OnLogEvent(event.get());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+ EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+ event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+ processor->OnLogEvent(event.get());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, ADB_DUMP,
+ &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size());
+
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ EXPECT_EQ(4, countMetrics.data_size());
+
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ // Partial bucket as metric is deactivated.
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+ data.bucket_info(0).end_bucket_elapsed_nanos());
+
+}
+
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index bf58b9c..60bd4a7 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/matchers/SimpleLogMatchingTracker.h"
#include "src/metrics/GaugeMetricProducer.h"
#include "src/stats_log_util.h"
#include "logd/LogEvent.h"
@@ -40,6 +41,8 @@
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
+const int64_t atomMatcherId = 678;
+const int logEventMatcherIndex = 0;
const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
@@ -61,11 +64,19 @@
gaugeFieldMatcher->add_child()->set_field(3);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// statsd started long ago.
// The metric starts in the middle of the bucket
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
-1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
pullerManager);
@@ -86,6 +97,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -103,6 +120,7 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
@@ -178,7 +196,15 @@
alert.set_num_buckets(100);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
-1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
@@ -246,6 +272,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -263,6 +295,7 @@
}));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
@@ -315,6 +348,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -330,7 +369,8 @@
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, -1, tagId,
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -388,6 +428,12 @@
dim->set_field(conditionTag);
dim->add_child()->set_field(1);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, _, _, _, _, _))
.WillRepeatedly(
@@ -420,7 +466,8 @@
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, -1, tagId,
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
@@ -463,7 +510,15 @@
auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
gaugeFieldMatcher->set_field(tagId);
gaugeFieldMatcher->add_child()->set_field(2);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
@@ -542,6 +597,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
@@ -574,6 +635,7 @@
int triggerId = 5;
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
@@ -632,6 +694,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+ new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
@@ -667,6 +735,7 @@
int triggerId = 5;
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index b380b03..dd00561 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -17,6 +17,7 @@
#include <unistd.h>
#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
#include "src/shell/ShellSubscriber.h"
#include "tests/metrics/metrics_test_helper.h"
@@ -26,7 +27,10 @@
using namespace android::os::statsd;
using android::sp;
using std::vector;
+using testing::_;
+using testing::Invoke;
using testing::NaggyMock;
+using testing::StrictMock;
#ifdef __ANDROID__
@@ -51,7 +55,10 @@
}
};
-TEST(ShellSubscriberTest, testPushedSubscription) {
+void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
+ sp<MockStatsPullerManager> pullerManager,
+ const vector<std::shared_ptr<LogEvent>>& pushedEvents,
+ const ShellData& expectedData) {
// set up 2 pipes for read/write config and data
int fds_config[2];
ASSERT_EQ(0, pipe(fds_config));
@@ -59,10 +66,6 @@
int fds_data[2];
ASSERT_EQ(0, pipe(fds_data));
- // create a simple config to get screen events
- ShellSubscription config;
- config.add_pushed()->set_atom_id(29);
-
size_t bufferSize = config.ByteSize();
// write the config to pipe, first write size of the config
@@ -75,15 +78,9 @@
write(fds_config[1], buffer.data(), bufferSize);
close(fds_config[1]);
- // create a shell subscriber.
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap);
+ sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
- LogEvent event1(29, 1000);
- event1.write(2);
- event1.init();
-
// mimic a binder thread that a shell subscriber runs on. it would block.
std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver);
@@ -93,44 +90,127 @@
// let the shell subscriber to receive the config from pipe.
std::this_thread::sleep_for(100ms);
- // send a log event that matches the config.
- std::thread log_reader([&shellClient, &event1] { shellClient->onLogEvent(event1); });
- log_reader.detach();
+ if (pushedEvents.size() > 0) {
+ // send a log event that matches the config.
+ std::thread log_reader([&shellClient, &pushedEvents] {
+ for (const auto& event : pushedEvents) {
+ shellClient->onLogEvent(*event);
+ }
+ });
- if (log_reader.joinable()) {
- log_reader.join();
+ log_reader.detach();
+
+ if (log_reader.joinable()) {
+ log_reader.join();
+ }
}
// wait for the data to be written.
std::this_thread::sleep_for(100ms);
- // this is the expected screen event atom.
- Atom atom;
- atom.mutable_screen_state_changed()->set_state(
- ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
- int atom_size = atom.ByteSize();
+ int expected_data_size = expectedData.ByteSize();
// now read from the pipe. firstly read the atom size.
size_t dataSize = 0;
EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
- EXPECT_EQ(atom_size, (int)dataSize);
+ EXPECT_EQ(expected_data_size, (int)dataSize);
// then read that much data which is the atom in proto binary format
vector<uint8_t> dataBuffer(dataSize);
EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
// make sure the received bytes can be parsed to an atom
- Atom receivedAtom;
+ ShellData receivedAtom;
EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
// serialze the expected atom to bytes. and compare. to make sure they are the same.
- vector<uint8_t> atomBuffer(atom_size);
- atom.SerializeToArray(&atomBuffer[0], atom_size);
+ vector<uint8_t> atomBuffer(expected_data_size);
+ expectedData.SerializeToArray(&atomBuffer[0], expected_data_size);
EXPECT_EQ(atomBuffer, dataBuffer);
close(fds_data[0]);
}
+TEST(ShellSubscriberTest, testPushedSubscription) {
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ vector<std::shared_ptr<LogEvent>> pushedList;
+
+ std::shared_ptr<LogEvent> event1 =
+ std::make_shared<LogEvent>(29 /*screen_state_atom_id*/, 1000 /*timestamp*/);
+ event1->write(::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ event1->init();
+ pushedList.push_back(event1);
+
+ // create a simple config to get screen events
+ ShellSubscription config;
+ config.add_pushed()->set_atom_id(29);
+
+ // this is the expected screen event atom.
+ ShellData shellData;
+ shellData.add_atom()->mutable_screen_state_changed()->set_state(
+ ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+ runShellTest(config, uidMap, pullerManager, pushedList, shellData);
+}
+
+namespace {
+
+int kUid1 = 1000;
+int kUid2 = 2000;
+
+int kCpuTime1 = 100;
+int kCpuTime2 = 200;
+
+ShellData getExpectedShellData() {
+ ShellData shellData;
+ auto* atom1 = shellData.add_atom()->mutable_cpu_active_time();
+ atom1->set_uid(kUid1);
+ atom1->set_time_millis(kCpuTime1);
+
+ auto* atom2 = shellData.add_atom()->mutable_cpu_active_time();
+ atom2->set_uid(kUid2);
+ atom2->set_time_millis(kCpuTime2);
+
+ return shellData;
+}
+
+ShellSubscription getPulledConfig() {
+ ShellSubscription config;
+ auto* pull_config = config.add_pulled();
+ pull_config->mutable_matcher()->set_atom_id(10016);
+ pull_config->set_freq_millis(2000);
+ return config;
+}
+
+} // namespace
+
+TEST(ShellSubscriberTest, testPulledSubscription) {
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(10016, _, _))
+ .WillRepeatedly(
+ Invoke([](int tagId, int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, timeNs);
+ event->write(kUid1);
+ event->write(kCpuTime1);
+ event->init();
+ data->push_back(event);
+ // another event
+ event = make_shared<LogEvent>(tagId, timeNs);
+ event->write(kUid2);
+ event->write(kCpuTime2);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
+ getExpectedShellData());
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 2b7da6a..4f4dd01 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -319,7 +319,7 @@
int[] uids = new int[]{mUids[id]};
String[] tags = new String[]{"acquire"};
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
- StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name,
StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
@@ -335,7 +335,7 @@
int[] uids = new int[]{mUids[id]};
String[] tags = new String[]{"release"};
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
- StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name,
StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
diff --git a/cmds/statsd/tools/statsd-testdrive/Android.bp b/cmds/statsd/tools/statsd-testdrive/Android.bp
new file mode 100644
index 0000000..f566bc7
--- /dev/null
+++ b/cmds/statsd/tools/statsd-testdrive/Android.bp
@@ -0,0 +1,11 @@
+java_binary_host {
+ name: "statsd_testdrive",
+ manifest: "manifest.txt",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ "guava",
+ ],
+}
diff --git a/cmds/statsd/tools/statsd-testdrive/manifest.txt b/cmds/statsd/tools/statsd-testdrive/manifest.txt
new file mode 100644
index 0000000..0266d11
--- /dev/null
+++ b/cmds/statsd/tools/statsd-testdrive/manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.statsd.testdrive.TestDrive
diff --git a/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java b/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
new file mode 100644
index 0000000..ae3e5a1
--- /dev/null
+++ b/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
@@ -0,0 +1,286 @@
+/*
+ * 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.statsd.testdrive;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+
+import com.google.common.io.Files;
+import com.google.protobuf.TextFormat;
+import com.google.protobuf.TextFormat.ParseException;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class TestDrive {
+
+ public static final int PULL_ATOM_START = 10000;
+ public static final long ATOM_MATCHER_ID = 1234567;
+
+ public static final String UPDATE_CONFIG_CMD = "cmd stats config update";
+ public static final String DUMP_REPORT_CMD = "cmd stats dump-report";
+ public static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
+ public static final String CONFIG_UID = "2000"; // shell uid
+ public static final long CONFIG_ID = 54321;
+
+ private static boolean mIsPushedAtom = false;
+
+ private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
+
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
+ return;
+ }
+ int atomId;
+ try {
+ atomId = Integer.valueOf(args[0]);
+ } catch (NumberFormatException e) {
+ logger.log(Level.SEVERE, "Bad atom id provided: " + args[0]);
+ return;
+ }
+ if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
+ logger.log(Level.SEVERE, "No such atom found: " + args[0]);
+ return;
+ }
+ mIsPushedAtom = atomId < PULL_ATOM_START;
+
+ TestDrive testDrive = new TestDrive();
+ try {
+ StatsdConfig config = testDrive.createConfig(atomId);
+ if (config == null) {
+ logger.log(Level.SEVERE, "Failed to create valid config.");
+ return;
+ }
+ testDrive.pushConfig(config);
+ logger.info("Pushed the following config to statsd:");
+ logger.info(config.toString());
+ if (mIsPushedAtom) {
+ logger.info(
+ "Now please play with the device to trigger the event. All events should be dumped after 1 min ...");
+ Thread.sleep(60_000);
+ } else {
+ // wait for 2 min
+ logger.info("Now wait for 2 minutes ...");
+ Thread.sleep(120_000);
+ }
+ testDrive.dumpMetrics();
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to test drive: " + e.getMessage());
+ } finally {
+ testDrive.removeConfig();
+ }
+ }
+
+ private void pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+ File configFile = File.createTempFile("statsdconfig", ".config");
+ configFile.deleteOnExit();
+ Files.write(config.toByteArray(), configFile);
+ String remotePath = "/data/local/tmp/" + configFile.getName();
+ runCommand(null, "adb", "push", configFile.getAbsolutePath(), remotePath);
+ runCommand(
+ null, "adb", "shell", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
+ String.valueOf(CONFIG_ID));
+ }
+
+ private void removeConfig() {
+ try {
+ runCommand(null, "adb", "shell", REMOVE_CONFIG_CMD, String.valueOf(CONFIG_ID));
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
+ }
+ }
+
+ // Runs a shell command. Output should go to outputFile. Returns error string.
+ private String runCommand(File outputFile, String... commands)
+ throws IOException, InterruptedException {
+ // Run macro on target
+ ProcessBuilder pb = new ProcessBuilder(commands);
+ // pb.redirectErrorStream(true);
+
+ if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
+ pb.redirectOutput(outputFile);
+ }
+ Process process = pb.start();
+
+ // capture any errors
+ StringBuilder out = new StringBuilder();
+ // Read output
+ BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+ String line = null, previous = null;
+ while ((line = br.readLine()) != null) {
+ if (!line.equals(previous)) {
+ previous = line;
+ out.append(line).append('\n');
+ logger.fine(line);
+ }
+ }
+
+ // Check result
+ if (process.waitFor() == 0) {
+ logger.info("Success!");
+ } else {
+ // Abnormal termination: Log command parameters and output and throw ExecutionException
+ logger.log(Level.SEVERE, out.toString());
+ }
+ return out.toString();
+ }
+
+ private StatsdConfig createConfig(int atomId) {
+ try {
+ if (mIsPushedAtom) {
+ return createSimpleEventMetricConfig(atomId);
+ } else {
+ return createSimpleGaugeMetricConfig(atomId);
+ }
+ } catch (ParseException e) {
+ logger.log(
+ Level.SEVERE,
+ "Failed to parse the config! line: "
+ + e.getLine()
+ + " col: "
+ + e.getColumn()
+ + " "
+ + e.getMessage());
+ }
+ return null;
+ }
+
+ private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException {
+ StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig();
+ baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
+ return baseBuilder.build();
+ }
+
+ private StatsdConfig createSimpleGaugeMetricConfig(int atomId) throws ParseException {
+ StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig();
+ baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
+ return baseBuilder.build();
+ }
+
+ private AtomMatcher createAtomMatcher(int atomId) {
+ AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
+ atomMatcherBuilder
+ .setId(ATOM_MATCHER_ID)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
+ return atomMatcherBuilder.build();
+ }
+
+ private StatsdConfig.Builder getSimpleEventMetricBaseConfig() throws ParseException {
+ StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+ TextFormat.merge(EVENT_BASE_CONFIG_SRTR, builder);
+ return builder;
+ }
+
+ private StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException {
+ StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+ TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder);
+ return builder;
+ }
+
+ private ConfigMetricsReportList getReportList() throws Exception {
+ try {
+ File outputFile = File.createTempFile("statsdret", ".bin");
+ outputFile.deleteOnExit();
+ runCommand(
+ outputFile,
+ "adb",
+ "shell",
+ DUMP_REPORT_CMD,
+ String.valueOf(CONFIG_ID),
+ "--include_current_bucket",
+ "--proto");
+ ConfigMetricsReportList reportList =
+ ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
+ return reportList;
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ logger.log(
+ Level.SEVERE,
+ "Failed to fetch and parse the statsd output report. "
+ + "Perhaps there is not a valid statsd config for the requested "
+ + "uid="
+ + CONFIG_UID
+ + ", id="
+ + CONFIG_ID
+ + ".");
+ throw (e);
+ }
+ }
+
+ private void dumpMetrics() throws Exception {
+ ConfigMetricsReportList reportList = getReportList();
+ // We may get multiple reports. Take the last one.
+ ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+ // Really should be only one metric.
+ if (report.getMetricsCount() != 1) {
+ logger.log(Level.SEVERE, "Only one report metric expected, got "
+ + report.getMetricsCount());
+ return;
+ }
+
+ logger.info("Got following metric data dump:");
+ logger.info(report.getMetrics(0).toString());
+ }
+
+ private static final String EVENT_BASE_CONFIG_SRTR =
+ "id: 12345\n"
+ + "event_metric {\n"
+ + " id: 1111\n"
+ + " what: 1234567\n"
+ + "}\n"
+ + "allowed_log_source: \"AID_GRAPHICS\"\n"
+ + "allowed_log_source: \"AID_INCIDENTD\"\n"
+ + "allowed_log_source: \"AID_STATSD\"\n"
+ + "allowed_log_source: \"AID_RADIO\"\n"
+ + "allowed_log_source: \"com.android.systemui\"\n"
+ + "allowed_log_source: \"com.android.vending\"\n"
+ + "allowed_log_source: \"AID_SYSTEM\"\n"
+ + "allowed_log_source: \"AID_ROOT\"\n"
+ + "allowed_log_source: \"AID_BLUETOOTH\"\n"
+ + "\n"
+ + "hash_strings_in_metric_report: false";
+
+ private static final String GAUGE_BASE_CONFIG_STR =
+ "id: 56789\n"
+ + "gauge_metric {\n"
+ + " id: 2222\n"
+ + " what: 1234567\n"
+ + " gauge_fields_filter {\n"
+ + " include_all: true\n"
+ + " }\n"
+ + " bucket: ONE_MINUTE\n"
+ + "}\n"
+ + "allowed_log_source: \"AID_GRAPHICS\"\n"
+ + "allowed_log_source: \"AID_INCIDENTD\"\n"
+ + "allowed_log_source: \"AID_STATSD\"\n"
+ + "allowed_log_source: \"AID_RADIO\"\n"
+ + "allowed_log_source: \"com.android.systemui\"\n"
+ + "allowed_log_source: \"com.android.vending\"\n"
+ + "allowed_log_source: \"AID_SYSTEM\"\n"
+ + "allowed_log_source: \"AID_ROOT\"\n"
+ + "allowed_log_source: \"AID_BLUETOOTH\"\n"
+ + "\n"
+ + "hash_strings_in_metric_report: false";
+}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index b71274c..bbc3f35 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1383,62 +1383,6 @@
Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V
Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
-Landroid/system/Int32Ref;->value:I
-Landroid/system/OsConstants;-><init>()V
-Landroid/system/OsConstants;->AF_NETLINK:I
-Landroid/system/OsConstants;->AF_PACKET:I
-Landroid/system/OsConstants;->ARPHRD_ETHER:I
-Landroid/system/OsConstants;->ARPHRD_LOOPBACK:I
-Landroid/system/OsConstants;->CAP_TO_INDEX(I)I
-Landroid/system/OsConstants;->CAP_TO_MASK(I)I
-Landroid/system/OsConstants;->ENONET:I
-Landroid/system/OsConstants;->ETH_P_ALL:I
-Landroid/system/OsConstants;->ETH_P_ARP:I
-Landroid/system/OsConstants;->ETH_P_IP:I
-Landroid/system/OsConstants;->ETH_P_IPV6:I
-Landroid/system/OsConstants;->EUSERS:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REPLY:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REQUEST:I
-Landroid/system/OsConstants;->ICMP_ECHO:I
-Landroid/system/OsConstants;->ICMP_ECHOREPLY:I
-Landroid/system/OsConstants;->initConstants()V
-Landroid/system/OsConstants;->IP_MULTICAST_ALL:I
-Landroid/system/OsConstants;->IP_RECVTOS:I
-Landroid/system/OsConstants;->MAP_POPULATE:I
-Landroid/system/OsConstants;->NETLINK_NETFILTER:I
-Landroid/system/OsConstants;->NETLINK_ROUTE:I
-Landroid/system/OsConstants;->O_DIRECT:I
-Landroid/system/OsConstants;->placeholder()I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT:I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT_RAISE:I
-Landroid/system/OsConstants;->RLIMIT_NOFILE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_RULE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFINFO:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_PREFIX:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_LINK:I
-Landroid/system/OsConstants;->RTMGRP_NEIGH:I
-Landroid/system/OsConstants;->RTMGRP_NOTIFY:I
-Landroid/system/OsConstants;->RTMGRP_TC:I
-Landroid/system/OsConstants;->SO_DOMAIN:I
-Landroid/system/OsConstants;->SO_PROTOCOL:I
-Landroid/system/OsConstants;->SPLICE_F_MORE:I
-Landroid/system/OsConstants;->SPLICE_F_MOVE:I
-Landroid/system/OsConstants;->SPLICE_F_NONBLOCK:I
-Landroid/system/OsConstants;->TIOCOUTQ:I
-Landroid/system/OsConstants;->UDP_ENCAP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP_NON_IKE:I
-Landroid/system/OsConstants;->UNIX_PATH_MAX:I
-Landroid/system/OsConstants;->XATTR_CREATE:I
-Landroid/system/OsConstants;->XATTR_REPLACE:I
-Landroid/system/OsConstants;->_LINUX_CAPABILITY_VERSION_3:I
-Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telephony/TelephonyManager$MultiSimVariants;
Landroid/util/Singleton;-><init>()V
@@ -2339,93 +2283,6 @@
Lcom/google/android/mms/util/PduCache;->purgeAll()V
Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
-Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
-Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;-><init>(IILjava/lang/String;)V
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mMessage:Ljava/lang/String;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyState:I
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyViolated:I
-Ldalvik/system/BlockGuard$Policy;->onNetwork()V
-Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V
-Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->LAX_POLICY:Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->setThreadPolicy(Ldalvik/system/BlockGuard$Policy;)V
-Ldalvik/system/BlockGuard;->threadPolicy:Ljava/lang/ThreadLocal;
-Ldalvik/system/CloseGuard$DefaultReporter;-><init>()V
-Ldalvik/system/CloseGuard$Reporter;->report(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ldalvik/system/CloseGuard;-><init>()V
-Ldalvik/system/CloseGuard;->close()V
-Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
-Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
-Ldalvik/system/CloseGuard;->setEnabled(Z)V
-Ldalvik/system/CloseGuard;->setReporter(Ldalvik/system/CloseGuard$Reporter;)V
-Ldalvik/system/CloseGuard;->warnIfOpen()V
-Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String;
-Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
-Ldalvik/system/DexFile;->isBackedByOatFile()Z
-Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
-Ldalvik/system/DexFile;->loadDex(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
-Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFileNative(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V
-Ldalvik/system/DexPathList$Element;-><init>(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V
-Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList$Element;->path:Ljava/io/File;
-Ldalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V
-Ldalvik/system/DexPathList$NativeLibraryElement;->path:Ljava/io/File;
-Ldalvik/system/DexPathList;-><init>(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addNativePath(Ljava/util/Collection;)V
-Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader;
-Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->dexElementsSuppressedExceptions:[Ljava/io/IOException;
-Ldalvik/system/DexPathList;->loadDexFile(Ljava/io/File;Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makeInMemoryDexElements([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->splitPaths(Ljava/lang/String;Z)Ljava/util/List;
-Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/SocketTagger;->get()Ldalvik/system/SocketTagger;
-Ldalvik/system/SocketTagger;->tag(Ljava/net/Socket;)V
-Ldalvik/system/SocketTagger;->untag(Ljava/net/Socket;)V
-Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V
-Ldalvik/system/VMDebug;->dumpReferenceTables()V
-Ldalvik/system/VMDebug;->isDebuggerConnected()Z
-Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
-Ldalvik/system/VMRuntime;->clearGrowthLimit()V
-Ldalvik/system/VMRuntime;->gcSoftReferences()V
-Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getExternalBytesAllocated()J
-Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getMinimumHeapSize()J
-Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
-Ldalvik/system/VMRuntime;->is64Bit()Z
-Ldalvik/system/VMRuntime;->is64BitAbi(Ljava/lang/String;)Z
-Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
-Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
-Ldalvik/system/VMRuntime;->registerNativeFree(I)V
-Ldalvik/system/VMRuntime;->runFinalization(J)V
-Ldalvik/system/VMRuntime;->runFinalizationSync()V
-Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
-Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
-Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
-Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
-Ldalvik/system/VMRuntime;->trackExternalFree(J)V
-Ldalvik/system/VMRuntime;->vmInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->vmLibrary()Ljava/lang/String;
-Ldalvik/system/VMStack;->fillStackTraceElements(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I
-Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
-Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
-Ldalvik/system/VMStack;->getThreadStackTrace(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;
Ljava/io/Console;->encoding()Ljava/lang/String;
Ljava/io/File;->filePath:Ljava/nio/file/Path;
Ljava/io/File;->fs:Ljava/io/FileSystem;
@@ -2501,18 +2358,6 @@
Ljava/lang/Class;->objectSize:I
Ljava/lang/Class;->status:I
Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
-Ljava/lang/Daemons$Daemon;->isRunning()Z
-Ljava/lang/Daemons$Daemon;->start()V
-Ljava/lang/Daemons$Daemon;->stop()V
-Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
-Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
-Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
-Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
-Ljava/lang/Daemons$ReferenceQueueDaemon;->INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon;
-Ljava/lang/Daemons;->MAX_FINALIZE_NANOS:J
-Ljava/lang/Daemons;->requestHeapTrim()V
-Ljava/lang/Daemons;->start()V
-Ljava/lang/Daemons;->stop()V
Ljava/lang/Double;->value:D
Ljava/lang/Enum;->getSharedConstants(Ljava/lang/Class;)[Ljava/lang/Enum;
Ljava/lang/Enum;->name:Ljava/lang/String;
@@ -2522,11 +2367,6 @@
Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V
Ljava/lang/Long;->value:J
Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
-Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
-Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->queue:Ljava/lang/ref/ReferenceQueue;
-Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V
Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
@@ -2669,12 +2509,6 @@
Ljava/nio/charset/Charset;->defaultCharset:Ljava/nio/charset/Charset;
Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
-Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
-Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
-Ljava/nio/NioUtils;->freeDirectBuffer(Ljava/nio/ByteBuffer;)V
-Ljava/nio/NioUtils;->unsafeArray(Ljava/nio/ByteBuffer;)[B
-Ljava/nio/NioUtils;->unsafeArrayOffset(Ljava/nio/ByteBuffer;)I
Ljava/security/KeyPairGenerator;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/KeyPairGenerator;
Ljava/security/KeyStore;->keyStoreSpi:Ljava/security/KeyStoreSpi;
Ljava/security/Signature;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/Signature;
@@ -2790,19 +2624,6 @@
Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
-Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/io/Libcore;->os:Llibcore/io/Os;
-Llibcore/io/Memory;->peekByte(J)B
-Llibcore/io/Memory;->peekByteArray(J[BII)V
-Llibcore/io/Memory;->peekInt(JZ)I
-Llibcore/io/Memory;->peekLong(JZ)J
-Llibcore/io/Memory;->pokeByte(JB)V
-Llibcore/io/Memory;->pokeByteArray(J[BII)V
-Llibcore/io/Memory;->pokeInt(JIZ)V
-Llibcore/io/Memory;->pokeLong(JJZ)V
-Llibcore/io/Streams;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)I
-Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
-Llibcore/util/ZoneInfo;->mTransitions:[J
Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String;
Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I
Lorg/ccil/cowan/tagsoup/ElementType;->theAtts:Lorg/ccil/cowan/tagsoup/AttributesImpl;
@@ -2821,100 +2642,6 @@
Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
Lorg/ccil/cowan/tagsoup/Schema;->theRoot:Lorg/ccil/cowan/tagsoup/ElementType;
Lorg/ccil/cowan/tagsoup/Schema;->theURI:Ljava/lang/String;
-Lorg/json/JSONArray;->values:Ljava/util/List;
-Lorg/json/JSONArray;->writeTo(Lorg/json/JSONStringer;)V
-Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
-Lorg/json/JSONObject;->checkName(Ljava/lang/String;)Ljava/lang/String;
-Lorg/json/JSONObject;->keySet()Ljava/util/Set;
-Lorg/json/JSONObject;->nameValuePairs:Ljava/util/LinkedHashMap;
-Lorg/json/JSONObject;->NEGATIVE_ZERO:Ljava/lang/Double;
-Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
-Lorg/json/JSONStringer;-><init>(I)V
-Lorg/json/JSONStringer;->beforeKey()V
-Lorg/json/JSONStringer;->beforeValue()V
-Lorg/json/JSONStringer;->close(Lorg/json/JSONStringer$Scope;Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
-Lorg/json/JSONStringer;->indent:Ljava/lang/String;
-Lorg/json/JSONStringer;->newline()V
-Lorg/json/JSONStringer;->open(Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
-Lorg/json/JSONStringer;->out:Ljava/lang/StringBuilder;
-Lorg/json/JSONStringer;->peek()Lorg/json/JSONStringer$Scope;
-Lorg/json/JSONStringer;->replaceTop(Lorg/json/JSONStringer$Scope;)V
-Lorg/json/JSONStringer;->stack:Ljava/util/List;
-Lorg/json/JSONStringer;->string(Ljava/lang/String;)V
-Lorg/json/JSONTokener;->in:Ljava/lang/String;
-Lorg/json/JSONTokener;->nextCleanInternal()I
-Lorg/json/JSONTokener;->nextToInternal(Ljava/lang/String;)Ljava/lang/String;
-Lorg/json/JSONTokener;->pos:I
-Lorg/json/JSONTokener;->readArray()Lorg/json/JSONArray;
-Lorg/json/JSONTokener;->readEscapeCharacter()C
-Lorg/json/JSONTokener;->readLiteral()Ljava/lang/Object;
-Lorg/json/JSONTokener;->readObject()Lorg/json/JSONObject;
-Lorg/json/JSONTokener;->skipToEndOfLine()V
-Lorg/w3c/dom/ls/LSSerializerFilter;->getWhatToShow()I
-Lorg/w3c/dom/traversal/NodeFilter;->acceptNode(Lorg/w3c/dom/Node;)S
-Lorg/w3c/dom/traversal/NodeIterator;->detach()V
-Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node;
-Lorg/xml/sax/ext/Attributes2Impl;->declared:[Z
-Lorg/xml/sax/ext/Attributes2Impl;->specified:[Z
-Lorg/xml/sax/ext/Locator2Impl;->encoding:Ljava/lang/String;
-Lorg/xml/sax/ext/Locator2Impl;->version:Ljava/lang/String;
-Lorg/xml/sax/helpers/AttributesImpl;->badIndex(I)V
-Lorg/xml/sax/helpers/AttributesImpl;->data:[Ljava/lang/String;
-Lorg/xml/sax/helpers/AttributesImpl;->ensureCapacity(I)V
-Lorg/xml/sax/helpers/AttributesImpl;->length:I
-Lorg/xml/sax/helpers/LocatorImpl;->columnNumber:I
-Lorg/xml/sax/helpers/LocatorImpl;->lineNumber:I
-Lorg/xml/sax/helpers/LocatorImpl;->publicId:Ljava/lang/String;
-Lorg/xml/sax/helpers/LocatorImpl;->systemId:Ljava/lang/String;
-Lorg/xml/sax/helpers/NamespaceSupport;->contextPos:I
-Lorg/xml/sax/helpers/NamespaceSupport;->contexts:[Lorg/xml/sax/helpers/NamespaceSupport$Context;
-Lorg/xml/sax/helpers/NamespaceSupport;->currentContext:Lorg/xml/sax/helpers/NamespaceSupport$Context;
-Lorg/xml/sax/helpers/NamespaceSupport;->EMPTY_ENUMERATION:Ljava/util/Enumeration;
-Lorg/xml/sax/helpers/NamespaceSupport;->namespaceDeclUris:Z
-Lorg/xml/sax/helpers/ParserAdapter;->attAdapter:Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;
-Lorg/xml/sax/helpers/ParserAdapter;->atts:Lorg/xml/sax/helpers/AttributesImpl;
-Lorg/xml/sax/helpers/ParserAdapter;->checkNotParsing(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/ParserAdapter;->contentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->dtdHandler:Lorg/xml/sax/DTDHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->entityResolver:Lorg/xml/sax/EntityResolver;
-Lorg/xml/sax/helpers/ParserAdapter;->errorHandler:Lorg/xml/sax/ErrorHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->locator:Lorg/xml/sax/Locator;
-Lorg/xml/sax/helpers/ParserAdapter;->makeException(Ljava/lang/String;)Lorg/xml/sax/SAXParseException;
-Lorg/xml/sax/helpers/ParserAdapter;->nameParts:[Ljava/lang/String;
-Lorg/xml/sax/helpers/ParserAdapter;->namespaces:Z
-Lorg/xml/sax/helpers/ParserAdapter;->nsSupport:Lorg/xml/sax/helpers/NamespaceSupport;
-Lorg/xml/sax/helpers/ParserAdapter;->parser:Lorg/xml/sax/Parser;
-Lorg/xml/sax/helpers/ParserAdapter;->parsing:Z
-Lorg/xml/sax/helpers/ParserAdapter;->prefixes:Z
-Lorg/xml/sax/helpers/ParserAdapter;->processName(Ljava/lang/String;ZZ)[Ljava/lang/String;
-Lorg/xml/sax/helpers/ParserAdapter;->reportError(Ljava/lang/String;)V
-Lorg/xml/sax/helpers/ParserAdapter;->setup(Lorg/xml/sax/Parser;)V
-Lorg/xml/sax/helpers/ParserAdapter;->setupParser()V
-Lorg/xml/sax/helpers/ParserAdapter;->uris:Z
-Lorg/xml/sax/helpers/XMLFilterImpl;->contentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->dtdHandler:Lorg/xml/sax/DTDHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->entityResolver:Lorg/xml/sax/EntityResolver;
-Lorg/xml/sax/helpers/XMLFilterImpl;->errorHandler:Lorg/xml/sax/ErrorHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->locator:Lorg/xml/sax/Locator;
-Lorg/xml/sax/helpers/XMLFilterImpl;->parent:Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/helpers/XMLFilterImpl;->setupParse()V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->documentHandler:Lorg/xml/sax/DocumentHandler;
-Lorg/xml/sax/helpers/XMLReaderAdapter;->qAtts:Lorg/xml/sax/helpers/XMLReaderAdapter$AttributesAdapter;
-Lorg/xml/sax/helpers/XMLReaderAdapter;->setup(Lorg/xml/sax/XMLReader;)V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->setupXMLReader()V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->xmlReader:Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/helpers/XMLReaderFactory;->loadClass(Ljava/lang/ClassLoader;Ljava/lang/String;)Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/InputSource;->byteStream:Ljava/io/InputStream;
-Lorg/xml/sax/InputSource;->characterStream:Ljava/io/Reader;
-Lorg/xml/sax/InputSource;->encoding:Ljava/lang/String;
-Lorg/xml/sax/InputSource;->publicId:Ljava/lang/String;
-Lorg/xml/sax/InputSource;->systemId:Ljava/lang/String;
-Lorg/xml/sax/SAXException;->exception:Ljava/lang/Exception;
-Lorg/xml/sax/SAXParseException;->columnNumber:I
-Lorg/xml/sax/SAXParseException;->init(Ljava/lang/String;Ljava/lang/String;II)V
-Lorg/xml/sax/SAXParseException;->lineNumber:I
-Lorg/xml/sax/SAXParseException;->publicId:Ljava/lang/String;
-Lorg/xml/sax/SAXParseException;->systemId:Ljava/lang/String;
Lsun/misc/Cleaner;->clean()V
Lsun/misc/Unsafe;->addressSize()I
Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-max-sdk-p-blacklist.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/hiddenapi-max-sdk-p-blacklist.txt
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 45e38cf9..e5e64d3 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -122,14 +122,6 @@
Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
Landroid/os/UserManager;->isAdminUser()Z
Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/system/NetlinkSocketAddress;-><init>(II)V
-Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->sendto(Ljava/io/FileDescriptor;[BIIILjava/net/SocketAddress;)I
-Landroid/system/Os;->setsockoptIfreq(Ljava/io/FileDescriptor;IILjava/lang/String;)V
-Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Landroid/system/PacketSocketAddress;-><init>(I[B)V
-Landroid/system/PacketSocketAddress;-><init>(SI)V
Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
Landroid/telephony/ims/compat/ImsService;-><init>()V
Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 56ca98f..1a8a32e 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2354,6 +2354,7 @@
android.nfc.NfcAdapter$CreateNdefMessageCallback
android.nfc.NfcManager
android.opengl.EGL14
+android.opengl.EGL15
android.opengl.EGLConfig
android.opengl.EGLContext
android.opengl.EGLDisplay
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aca80b4..3cc5e37 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4678,7 +4678,7 @@
if (decor != null) {
decor.cancelPendingInputEvents();
}
- if (options != null && !isTopOfTask()) {
+ if (options != null) {
mActivityTransitionState.startExitOutTransition(this, options);
}
}
@@ -4882,6 +4882,7 @@
Bundle options)
throws IntentSender.SendIntentException {
try {
+ options = transferSpringboardActivityOptions(options);
String resolvedType = null;
if (fillInIntent != null) {
fillInIntent.migrateExtraStreamToClipData();
@@ -4898,6 +4899,12 @@
throw new IntentSender.SendIntentException();
}
Instrumentation.checkStartActivityResult(result, null);
+
+ if (options != null) {
+ // Only when the options are not null, as the intent can point to something other
+ // than an Activity.
+ cancelInputsAndStartExitTransition(options);
+ }
} catch (RemoteException e) {
}
if (requestCode >= 0) {
@@ -6471,7 +6478,7 @@
*
* @return true if this is the topmost, non-finishing activity in its task.
*/
- private boolean isTopOfTask() {
+ final boolean isTopOfTask() {
if (mToken == null || mWindow == null) {
return false;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 14b8ae4..7330da3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -137,6 +137,17 @@
private static final int FIRST_START_NON_FATAL_ERROR_CODE = 100;
private static final int LAST_START_NON_FATAL_ERROR_CODE = 199;
+ /**
+ * Disable hidden API checks for the newly started instrumentation.
+ * @hide
+ */
+ public static final int INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+ /**
+ * Mount full external storage for the newly started instrumentation.
+ * @hide
+ */
+ public static final int INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL = 1 << 1;
+
static final class UidObserver extends IUidObserver.Stub {
final OnUidImportanceListener mListener;
final Context mContext;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 784ce04..069effd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -22,6 +22,7 @@
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.os.Bundle;
@@ -123,17 +124,6 @@
public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
/**
- * Saves the current activity manager state and includes the saved state in the next dump of
- * activity manager.
- */
- public abstract void saveANRState(String reason);
-
- /**
- * Clears the previously saved activity manager ANR state.
- */
- public abstract void clearSavedANRState();
-
- /**
* @return true if runtime was restarted, false if it's normal boot
*/
public abstract boolean isRuntimeRestarted();
@@ -165,10 +155,19 @@
/**
* Returns a list that contains the memory stats for currently running processes.
+ *
+ * Only processes managed by ActivityManagerService are included.
*/
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
/**
+ * Returns a list that contains the memory stats for monitored native processes.
+ *
+ * The list of the monitored processes is defined in MemoryStatUtil class.
+ */
+ public abstract List<ProcessMemoryState> getMemoryStateForNativeProcesses();
+
+ /**
* Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
* needed.
*/
@@ -187,9 +186,6 @@
/** Trims memory usage in the system by removing/stopping unused application processes. */
public abstract void trimApplications();
- /** Closes all system dialogs. */
- public abstract void closeSystemDialogs(String reason);
-
/** Kill the processes in the list due to their tasks been removed. */
public abstract void killProcessesForRemovedTask(ArrayList<Object> procsToKill);
@@ -242,4 +238,46 @@
throws TransactionTooLargeException;
public abstract void disconnectActivityFromServices(Object connectionHolder);
+ public abstract void cleanUpServices(int userId, ComponentName component, Intent baseIntent);
+ public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId);
+ public abstract void ensureBootCompleted();
+ public abstract void updateOomLevelsForDisplay(int displayId);
+ public abstract boolean isActivityStartsLoggingEnabled();
+ public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
+
+ /** Input dispatch timeout to a window, start the ANR process. */
+ public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
+ public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
+ ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
+ boolean aboveSystem, String reason);
+
+ /**
+ * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
+ * flags.
+ */
+ public abstract void broadcastGlobalConfigurationChanged(int changes, boolean initLocale);
+
+ /**
+ * Sends {@link android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} with all the appropriate
+ * flags.
+ */
+ public abstract void broadcastCloseSystemDialogs(String reason);
+
+ /**
+ * Kills all background processes, except those matching any of the specified properties.
+ *
+ * @param minTargetSdk the target SDK version at or above which to preserve processes,
+ * or {@code -1} to ignore the target SDK
+ * @param maxProcState the process state at or below which to preserve processes,
+ * or {@code -1} to ignore the process state
+ */
+ public abstract void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState);
+
+ /** Starts a given process. */
+ public abstract void startProcess(String processName, ApplicationInfo info,
+ boolean knownToBeDead, String hostingType, ComponentName hostingName);
+
+ /** Starts up the starting activity process for debugging if needed. */
+ public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+ ProfilerInfo profilerInfo);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 3c9a2d4..94b42ff 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -195,6 +195,13 @@
private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
/**
+ * See {@link #setPendingIntentLaunchFlags(int)}
+ * @hide
+ */
+ private static final String KEY_PENDING_INTENT_LAUNCH_FLAGS =
+ "android.activity.pendingIntentLaunchFlags";
+
+ /**
* See {@link #setTaskOverlay}.
* @hide
*/
@@ -309,6 +316,7 @@
@WindowConfiguration.ActivityType
private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED;
private int mLaunchTaskId = -1;
+ private int mPendingIntentLaunchFlags;
private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
private boolean mLockTaskMode = false;
private boolean mDisallowEnterPictureInPictureWhileLaunching;
@@ -932,6 +940,7 @@
mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
+ mPendingIntentLaunchFlags = opts.getInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, 0);
mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
@@ -1233,6 +1242,22 @@
}
/**
+ * Specifies intent flags to be applied for any activity started from a PendingIntent.
+ *
+ * @hide
+ */
+ public void setPendingIntentLaunchFlags(@android.content.Intent.Flags int flags) {
+ mPendingIntentLaunchFlags = flags;
+ }
+
+ /**
+ * @hide
+ */
+ public int getPendingIntentLaunchFlags() {
+ return mPendingIntentLaunchFlags;
+ }
+
+ /**
* Set's whether the activity launched with this option should be a task overlay. That is the
* activity will always be the top activity of the task. If {@param canResume} is true, then
* the task will also not be moved to the front of the stack.
@@ -1463,6 +1488,9 @@
if (mLaunchTaskId != -1) {
b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
}
+ if (mPendingIntentLaunchFlags != 0) {
+ b.putInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, mPendingIntentLaunchFlags);
+ }
if (mTaskOverlay) {
b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index af8aa4e..b8fe2f1 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -45,6 +45,12 @@
public static final int INVALID_STACK_ID = -1;
/**
+ * Invalid task ID.
+ * @hide
+ */
+ public static final int INVALID_TASK_ID = -1;
+
+ /**
* Parameter to {@link IActivityTaskManager#setTaskWindowingModeSplitScreenPrimary} which
* specifies the position of the created docked stack at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ff6aca6..9d3c5c6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5054,7 +5054,7 @@
private void performConfigurationChangedForActivity(ActivityClientRecord r,
Configuration newBaseConfig) {
performConfigurationChangedForActivity(r, newBaseConfig,
- r.activity.getDisplay().getDisplayId(), false /* movedToDifferentDisplay */);
+ r.activity.getDisplayId(), false /* movedToDifferentDisplay */);
}
/**
@@ -5406,7 +5406,7 @@
return;
}
final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
- && displayId != r.activity.getDisplay().getDisplayId();
+ && displayId != r.activity.getDisplayId();
// Perform updates.
r.overrideConfig = overrideConfig;
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 9b2bfc5..4b87a64 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -193,6 +193,13 @@
*/
public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
+ /**
+ * Sent by Activity#startActivity to notify the entering activity that enter animation for
+ * back is allowed. If this message is not received, the default exit animation will run when
+ * backing out of an activity (instead of the 'reverse' shared element transition).
+ */
+ public static final int MSG_ALLOW_RETURN_TRANSITION = 108;
+
private Window mWindow;
final protected ArrayList<String> mAllSharedElementNames;
final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -346,8 +353,6 @@
return new ArrayList<View>(mSharedElements);
}
- public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
-
protected Transition setTargets(Transition transition, boolean add) {
if (transition == null || (add &&
(mTransitioningViews == null || mTransitioningViews.isEmpty()))) {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index b8f5a8e..3201feb 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -35,7 +35,7 @@
*/
class ActivityTransitionState {
- private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";
+ private static final String PENDING_EXIT_SHARED_ELEMENTS = "android:pendingExitSharedElements";
private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom";
@@ -43,9 +43,9 @@
/**
* The shared elements that the calling Activity has said that they transferred to this
- * Activity.
+ * Activity and will be transferred back during exit animation.
*/
- private ArrayList<String> mEnteringNames;
+ private ArrayList<String> mPendingExitNames;
/**
* The names of shared elements that were shared to the called Activity.
@@ -112,8 +112,7 @@
public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) {
if (mExitTransitionCoordinators == null) {
- mExitTransitionCoordinators =
- new SparseArray<WeakReference<ExitTransitionCoordinator>>();
+ mExitTransitionCoordinators = new SparseArray<>();
}
WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator);
// clean up old references:
@@ -132,7 +131,7 @@
public void readState(Bundle bundle) {
if (bundle != null) {
if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
- mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
+ mPendingExitNames = bundle.getStringArrayList(PENDING_EXIT_SHARED_ELEMENTS);
}
if (mEnterTransitionCoordinator == null) {
mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
@@ -141,9 +140,21 @@
}
}
+ /**
+ * Returns the element names to be used for exit animation. It caches the list internally so
+ * that it is preserved through activty destroy and restore.
+ */
+ private ArrayList<String> getPendingExitNames() {
+ if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
+ mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
+ }
+ return mPendingExitNames;
+ }
+
public void saveState(Bundle bundle) {
- if (mEnteringNames != null) {
- bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
+ ArrayList<String> pendingExitNames = getPendingExitNames();
+ if (pendingExitNames != null) {
+ bundle.putStringArrayList(PENDING_EXIT_SHARED_ELEMENTS, pendingExitNames);
}
if (mExitingFrom != null) {
bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
@@ -226,7 +237,7 @@
}
} else {
mEnterTransitionCoordinator.namedViewsReady(null, null);
- mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
+ mPendingExitNames = null;
}
mExitingFrom = null;
@@ -268,7 +279,7 @@
}
public void clear() {
- mEnteringNames = null;
+ mPendingExitNames = null;
mExitingFrom = null;
mExitingTo = null;
mExitingToView = null;
@@ -296,7 +307,8 @@
}
public boolean startExitBackTransition(final Activity activity) {
- if (mEnteringNames == null || mCalledExitCoordinator != null) {
+ ArrayList<String> pendingExitNames = getPendingExitNames();
+ if (pendingExitNames == null || mCalledExitCoordinator != null) {
return false;
} else {
if (!mHasExited) {
@@ -315,7 +327,7 @@
}
mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
- activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames,
+ activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames,
null, null, true);
if (enterViewsTransition != null && decor != null) {
enterViewsTransition.resume(decor);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a05d01b..a30ae79 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -431,9 +431,11 @@
public static final int OP_BLUETOOTH_SCAN = 77;
/** @hide Use the BiometricPrompt/BiometricManager APIs. */
public static final int OP_USE_BIOMETRIC = 78;
+ /** @hide Physical activity recognition. */
+ public static final int OP_ACTIVITY_RECOGNITION = 79;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 79;
+ public static final int _NUM_OP = 80;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -681,6 +683,9 @@
/** @hide Use the BiometricPrompt/BiometricManager APIs. */
public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
+ /** @hide Recognize physical activity. */
+ public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
+
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -722,6 +727,8 @@
OP_CAMERA,
// Body sensors
OP_BODY_SENSORS,
+ // Activity recognition
+ OP_ACTIVITY_RECOGNITION,
// APPOP PERMISSIONS
OP_ACCESS_NOTIFICATIONS,
@@ -819,6 +826,7 @@
OP_START_FOREGROUND, // START_FOREGROUND
OP_COARSE_LOCATION, // BLUETOOTH_SCAN
OP_USE_BIOMETRIC, // BIOMETRIC
+ OP_ACTIVITY_RECOGNITION, // ACTIVITY_RECOGNITION
};
/**
@@ -904,6 +912,7 @@
OPSTR_START_FOREGROUND,
OPSTR_BLUETOOTH_SCAN,
OPSTR_USE_BIOMETRIC,
+ OPSTR_ACTIVITY_RECOGNITION,
};
/**
@@ -990,6 +999,7 @@
"START_FOREGROUND",
"BLUETOOTH_SCAN",
"USE_BIOMETRIC",
+ "ACTIVITY_RECOGNITION",
};
/**
@@ -1077,6 +1087,7 @@
Manifest.permission.FOREGROUND_SERVICE,
null, // no permission for OP_BLUETOOTH_SCAN
Manifest.permission.USE_BIOMETRIC,
+ Manifest.permission.ACTIVITY_RECOGNITION,
};
/**
@@ -1164,6 +1175,7 @@
null, // START_FOREGROUND
null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
null, // USE_BIOMETRIC
+ null, // ACTIVITY_RECOGNITION
};
/**
@@ -1250,6 +1262,7 @@
false, // START_FOREGROUND
true, // BLUETOOTH_SCAN
false, // USE_BIOMETRIC
+ false, // ACTIVITY_RECOGNITION
};
/**
@@ -1335,6 +1348,7 @@
AppOpsManager.MODE_ALLOWED, // START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // BLUETOOTH_SCAN
AppOpsManager.MODE_ALLOWED, // USE_BIOMETRIC
+ AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
};
/**
@@ -1424,6 +1438,7 @@
false, // START_FOREGROUND
false, // BLUETOOTH_SCAN
false, // USE_BIOMETRIC
+ false, // ACTIVITY_RECOGNITION
};
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 264029b..fcd9a05 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -55,6 +55,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
@@ -85,6 +86,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IconDrawableFactory;
import android.util.LauncherIcons;
@@ -2255,9 +2257,19 @@
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
String dialogMessage) {
+ final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage)
+ ? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build()
+ : null;
+ return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo);
+ }
+
+ @Override
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ PersistableBundle appExtras, PersistableBundle launcherExtras,
+ SuspendDialogInfo dialogInfo) {
try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
- launcherExtras, dialogMessage, mContext.getOpPackageName(),
+ launcherExtras, dialogInfo, mContext.getOpPackageName(),
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 77f6395..dc707e8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2088,8 +2088,7 @@
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
- final int displayId = mDisplay != null
- ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ final int displayId = getDisplayId();
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
@@ -2124,8 +2123,7 @@
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
flags, null);
- final int displayId = mDisplay != null
- ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ final int displayId = getDisplayId();
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
@@ -2152,8 +2150,7 @@
final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
mActivityToken, mUser, mFlags, classLoader);
- final int displayId = mDisplay != null
- ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ final int displayId = getDisplayId();
context.setResources(ResourcesManager.getInstance().getResources(
mActivityToken,
@@ -2177,7 +2174,7 @@
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
mActivityToken, mUser, mFlags, mClassLoader);
- final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ final int displayId = getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
return context;
@@ -2250,6 +2247,11 @@
}
@Override
+ public int getDisplayId() {
+ return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ }
+
+ @Override
public void updateDisplay(int displayId) {
mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources);
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index ab847fd..bce243c 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -65,6 +65,7 @@
private OneShotPreDrawListener mViewsReadyListener;
private final boolean mIsCrossTask;
private Drawable mReplacedBackground;
+ private ArrayList<String> mPendingExitNames;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
@@ -249,6 +250,11 @@
case MSG_CANCEL:
cancel();
break;
+ case MSG_ALLOW_RETURN_TRANSITION:
+ if (!mIsCanceled) {
+ mPendingExitNames = mAllSharedElementNames;
+ }
+ break;
}
}
@@ -256,6 +262,10 @@
return mIsReturning && mResultReceiver != null;
}
+ public ArrayList<String> getPendingExitSharedElementNames() {
+ return mPendingExitNames;
+ }
+
/**
* This is called onResume. If an Activity is resuming and the transitions
* haven't started yet, force the views to appear. This is likely to be
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index df31da9..48a711e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -433,6 +433,11 @@
if (!mSharedElementNotified) {
mSharedElementNotified = true;
delayCancel();
+
+ if (!mActivity.isTopOfTask()) {
+ mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null);
+ }
+
if (mListener == null) {
mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
notifyExitComplete();
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index ceb828b..0e428ae 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -1013,6 +1013,11 @@
replaceTargets(sharedElementTransition, sharedElementsIn, null);
}
}
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ }
});
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b57a7d9..4f41da6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -20,6 +20,7 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,7 @@
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -7759,8 +7761,17 @@
* @see Notification.Builder#setColorized(boolean)
*/
public static class MediaStyle extends Style {
+ // Changing max media buttons requires also changing templates
+ // (notification_template_material_media and notification_template_material_big_media).
static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
static final int MAX_MEDIA_BUTTONS = 5;
+ @IdRes private static final int[] MEDIA_BUTTON_IDS = {
+ R.id.action0,
+ R.id.action1,
+ R.id.action2,
+ R.id.action3,
+ R.id.action4,
+ };
private int[] mActionsToShowInCompact = null;
private MediaSession.Token mToken;
@@ -7874,15 +7885,16 @@
return false;
}
- private RemoteViews generateMediaActionButton(Action action, int color) {
+ private void bindMediaActionButton(RemoteViews container, @IdRes int buttonId,
+ Action action, int color) {
final boolean tombstone = (action.actionIntent == null);
- RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
- R.layout.notification_material_media_action);
- button.setImageViewIcon(R.id.action0, action.getIcon());
+ container.setViewVisibility(buttonId, View.VISIBLE);
+ container.setImageViewIcon(buttonId, action.getIcon());
// If the action buttons should not be tinted, then just use the default
// notification color. Otherwise, just use the passed-in color.
- Configuration currentConfig = mBuilder.mContext.getResources().getConfiguration();
+ Resources resources = mBuilder.mContext.getResources();
+ Configuration currentConfig = resources.getConfiguration();
boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
@@ -7890,13 +7902,21 @@
: ContrastColorUtil.resolveColor(mBuilder.mContext,
Notification.COLOR_DEFAULT, inNightMode);
- button.setDrawableTint(R.id.action0, false, tintColor,
+ container.setDrawableTint(buttonId, false, tintColor,
PorterDuff.Mode.SRC_ATOP);
+
+ final TypedArray typedArray = mBuilder.mContext.obtainStyledAttributes(
+ new int[]{ android.R.attr.colorControlHighlight });
+ int rippleAlpha = Color.alpha(typedArray.getColor(0, 0));
+ typedArray.recycle();
+ int rippleColor = Color.argb(rippleAlpha, Color.red(tintColor), Color.green(tintColor),
+ Color.blue(tintColor));
+ container.setRippleDrawableColor(buttonId, ColorStateList.valueOf(rippleColor));
+
if (!tombstone) {
- button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
+ container.setOnClickPendingIntent(buttonId, action.actionIntent);
}
- button.setContentDescription(R.id.action0, action.title);
- return button;
+ container.setContentDescription(buttonId, action.title);
}
private RemoteViews makeMediaContentView() {
@@ -7905,21 +7925,20 @@
null /* result */);
final int numActions = mBuilder.mActions.size();
- final int N = mActionsToShowInCompact == null
+ final int numActionsToShow = mActionsToShowInCompact == null
? 0
: Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
- view.removeAllViews(com.android.internal.R.id.media_actions);
- if (N > 0) {
- for (int i = 0; i < N; i++) {
- if (i >= numActions) {
- throw new IllegalArgumentException(String.format(
- "setShowActionsInCompactView: action %d out of bounds (max %d)",
- i, numActions - 1));
- }
-
+ if (numActionsToShow > numActions) {
+ throw new IllegalArgumentException(String.format(
+ "setShowActionsInCompactView: action %d out of bounds (max %d)",
+ numActions, numActions - 1));
+ }
+ for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
+ if (i < numActionsToShow) {
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
- final RemoteViews button = generateMediaActionButton(action, getActionColor());
- view.addView(com.android.internal.R.id.media_actions, button);
+ bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, getActionColor());
+ } else {
+ view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
}
handleImage(view);
@@ -7949,12 +7968,12 @@
RemoteViews big = mBuilder.applyStandardTemplate(
R.layout.notification_template_material_big_media, false, null /* result */);
- if (actionCount > 0) {
- big.removeAllViews(com.android.internal.R.id.media_actions);
- for (int i = 0; i < actionCount; i++) {
- final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
+ for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
+ if (i < actionCount) {
+ bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i),
getActionColor());
- big.addView(com.android.internal.R.id.media_actions, button);
+ } else {
+ big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
}
handleImage(big);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 848def6..9f93e17 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -603,9 +603,10 @@
}
/**
- * @hide
+ * Returns whether the user has chosen the importance of this channel, either to affirm the
+ * initial selection from the app, or changed it to be higher or lower.
*/
- public boolean isImportanceLocked() {
+ public boolean hasUserSetImportance() {
return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0044005..77cebc8 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -377,11 +377,15 @@
return new DisplayManager(ctx.getOuterContext());
}});
+ // InputMethodManager has its own cache strategy based on display id to support apps that
+ // still assume InputMethodManager is a per-process singleton and it's safe to directly
+ // access internal fields via reflection. Hence directly use ServiceFetcher instead of
+ // StaticServiceFetcher/CachedServiceFetcher.
registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class,
- new StaticServiceFetcher<InputMethodManager>() {
+ new ServiceFetcher<InputMethodManager>() {
@Override
- public InputMethodManager createService() {
- return InputMethodManager.getInstanceInternal();
+ public InputMethodManager getService(ContextImpl ctx) {
+ return InputMethodManager.forContext(ctx.getOuterContext());
}});
registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class,
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 9873a81..e33d1fe 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.app.slice.Slice;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -77,6 +78,7 @@
final int mContextDescriptionResource;
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
+ final String mSettingsSliceUri;
/**
* Constructor.
@@ -118,7 +120,6 @@
com.android.internal.R.styleable.Wallpaper);
mSettingsActivityName = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsActivity);
-
mThumbnailResource = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_thumbnail,
-1);
@@ -140,6 +141,8 @@
mSupportsAmbientMode = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
false);
+ mSettingsSliceUri = sa.getString(
+ com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
sa.recycle();
} catch (NameNotFoundException e) {
@@ -159,6 +162,7 @@
mContextDescriptionResource = source.readInt();
mShowMetadataInPreview = source.readInt() != 0;
mSupportsAmbientMode = source.readInt() != 0;
+ mSettingsSliceUri = source.readString();
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -332,13 +336,28 @@
* explicit {@link android.content.ComponentName}
* composed of {@link #getPackageName} and the class name returned here.
*
- * <p>A null will be returned if there is no settings activity associated
+ * <p>{@code null} will be returned if there is no settings activity associated
* with the wallpaper.
*/
public String getSettingsActivity() {
return mSettingsActivityName;
}
+ /**
+ * Returns an URI that provides a settings {@link Slice} for this wallpaper.
+ *
+ * <p>{@code null} will be returned if there is no settings Slice URI associated
+ * with the wallpaper.
+ *
+ * @return The URI.
+ */
+ public Uri getSettingsSliceUri() {
+ if (mSettingsSliceUri == null) {
+ return null;
+ }
+ return Uri.parse(mSettingsSliceUri);
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Service:");
mService.dump(pw, prefix + " ");
@@ -367,6 +386,7 @@
dest.writeInt(mContextDescriptionResource);
dest.writeInt(mShowMetadataInPreview ? 1 : 0);
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
+ dest.writeString(mSettingsSliceUri);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 74fb4df..92daf08 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -51,6 +51,7 @@
import android.graphics.Bitmap;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -1898,6 +1899,36 @@
public static final String ACTION_PROFILE_OWNER_CHANGED =
"android.app.action.PROFILE_OWNER_CHANGED";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"PRIVATE_DNS_MODE_"}, value = {
+ PRIVATE_DNS_MODE_UNKNOWN,
+ PRIVATE_DNS_MODE_OFF,
+ PRIVATE_DNS_MODE_OPPORTUNISTIC,
+ PRIVATE_DNS_MODE_PROVIDER_HOSTNAME
+ })
+ public @interface PrivateDnsMode {}
+
+ /**
+ * Specifies that the Private DNS setting is in an unknown state.
+ */
+ public static final int PRIVATE_DNS_MODE_UNKNOWN = 0;
+
+ /**
+ * Specifies that Private DNS was turned off completely.
+ */
+ public static final int PRIVATE_DNS_MODE_OFF = 1;
+
+ /**
+ * Specifies that the device owner requested opportunistic DNS over TLS
+ */
+ public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2;
+
+ /**
+ * Specifies that the device owner configured a specific host to use for Private DNS.
+ */
+ public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
+
/**
* Return true if the given administrator component is currently active (enabled) in the system.
*
@@ -5756,7 +5787,8 @@
}
if (mService != null) {
try {
- return mService.checkDeviceIdentifierAccess(packageName, userId);
+ return mService.checkDeviceIdentifierAccess(packageName, userId,
+ Binder.getCallingPid(), Binder.getCallingUid());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -9754,4 +9786,80 @@
throw re.rethrowFromSystemServer();
}
}
+
+
+ /**
+ * Sets the global Private DNS mode and host to be used.
+ * May only be called by the device owner.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param mode Which mode to set - either {@code PRIVATE_DNS_MODE_OPPORTUNISTIC} or
+ * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}.
+ * Since the opportunistic mode defaults to ordinary DNS lookups, the
+ * option to turn it completely off is not available, so this method
+ * may not be called with {@code PRIVATE_DNS_MODE_OFF}.
+ * @param privateDnsHost The hostname of a server that implements DNS over TLS (RFC7858), if
+ * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} was specified as the mode,
+ * null otherwise.
+ * @throws IllegalArgumentException in the following cases: if a {@code privateDnsHost} was
+ * provided but the mode was not {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}, if the mode
+ * specified was {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} but {@code privateDnsHost} does
+ * not look like a valid hostname, or if the mode specified is not one of the two valid modes.
+ *
+ * @throws SecurityException if the caller is not the device owner.
+ */
+ public void setGlobalPrivateDns(@NonNull ComponentName admin,
+ @PrivateDnsMode int mode, @Nullable String privateDnsHost) {
+ throwIfParentInstance("setGlobalPrivateDns");
+ if (mService == null) {
+ return;
+ }
+
+ try {
+ mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the system-wide Private DNS mode.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @return one of {@code PRIVATE_DNS_MODE_OFF}, {@code PRIVATE_DNS_MODE_OPPORTUNISTIC},
+ * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} or {@code PRIVATE_DNS_MODE_UNKNOWN}.
+ * @throws SecurityException if the caller is not the device owner.
+ */
+ public int getGlobalPrivateDnsMode(@NonNull ComponentName admin) {
+ throwIfParentInstance("setGlobalPrivateDns");
+ if (mService == null) {
+ return PRIVATE_DNS_MODE_UNKNOWN;
+ }
+
+ try {
+ return mService.getGlobalPrivateDnsMode(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the system-wide Private DNS host.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @return The hostname used for Private DNS queries.
+ * @throws SecurityException if the caller is not the device owner.
+ */
+ public String getGlobalPrivateDnsHost(@NonNull ComponentName admin) {
+ throwIfParentInstance("setGlobalPrivateDns");
+ if (mService == null) {
+ return null;
+ }
+
+ try {
+ return mService.getGlobalPrivateDnsHost(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5e45450..ce1f4ef 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -153,7 +153,7 @@
void clearProfileOwner(in ComponentName who);
boolean hasUserSetupCompleted();
- boolean checkDeviceIdentifierAccess(in String packageName, int userHandle);
+ boolean checkDeviceIdentifierAccess(in String packageName, int userHandle, int pid, int uid);
void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
CharSequence getDeviceOwnerLockScreenInfo();
@@ -413,4 +413,8 @@
boolean isOverrideApnEnabled(in ComponentName admin);
boolean isMeteredDataDisabledPackageForUser(in ComponentName admin, String packageName, int userId);
+
+ void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
+ int getGlobalPrivateDnsMode(in ComponentName admin);
+ String getGlobalPrivateDnsHost(in ComponentName admin);
}
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index fda2f89..cb996f3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -183,7 +184,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index e7c8944..c447868 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -23,6 +23,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -138,7 +139,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 9b7c173..d616b8f 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -57,6 +57,7 @@
private int mAuthRetryState;
private int mConnState;
private final Object mStateLock = new Object();
+ private final Object mDeviceBusyLock = new Object();
@UnsupportedAppUsage
private Boolean mDeviceBusy = false;
@UnsupportedAppUsage
@@ -282,7 +283,7 @@
}
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
}
@@ -357,7 +358,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -413,7 +414,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -496,7 +497,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -547,7 +548,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -596,7 +597,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -1098,7 +1099,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1132,7 +1133,7 @@
if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
if (mService == null || mClientIf == 0) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1178,7 +1179,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1221,7 +1222,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1262,7 +1263,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1330,7 +1331,7 @@
if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ec18d42..549c1fa 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -428,7 +429,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index b967fb2..22d41d9 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -491,7 +492,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 606f00a..47c4ee6 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -29,6 +29,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -205,7 +206,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
return;
}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 3bc8544..e44f36e 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -24,6 +24,7 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -454,7 +455,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 0ca39f1..58a2522 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -25,6 +25,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -279,7 +280,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 98c23c6..fc5f830 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -110,7 +111,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 559a59b..1c82e19 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -25,6 +25,7 @@
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -128,7 +129,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 58be732..8923d73 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -26,6 +26,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -150,7 +151,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index ae264e1..a601df0 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -24,6 +24,7 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -164,7 +165,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 1446adc..cbc96c0 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -23,6 +23,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -116,7 +117,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 1b73206..ebf6bed 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -148,7 +149,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
return false;
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 2a03787..145c9273 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -822,6 +822,48 @@
}
/**
+ * Opaque token representing the identity of an incoming IPC.
+ */
+ public final class CallingIdentity {
+ /** {@hide} */
+ public final long binderToken;
+ /** {@hide} */
+ public final String callingPackage;
+
+ /** {@hide} */
+ public CallingIdentity(long binderToken, String callingPackage) {
+ this.binderToken = binderToken;
+ this.callingPackage = callingPackage;
+ }
+ }
+
+ /**
+ * Reset the identity of the incoming IPC on the current thread.
+ * <p>
+ * Internally this calls {@link Binder#clearCallingIdentity()} and also
+ * clears any value stored in {@link #getCallingPackage()}.
+ *
+ * @return Returns an opaque token that can be used to restore the original
+ * calling identity by passing it to
+ * {@link #restoreCallingIdentity}.
+ */
+ public final @NonNull CallingIdentity clearCallingIdentity() {
+ return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+ }
+
+ /**
+ * Restore the identity of the incoming IPC on the current thread back to a
+ * previously identity that was returned by {@link #clearCallingIdentity}.
+ * <p>
+ * Internally this calls {@link Binder#restoreCallingIdentity(long)} and
+ * also restores any value stored in {@link #getCallingPackage()}.
+ */
+ public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
+ Binder.restoreCallingIdentity(identity.binderToken);
+ mCallingPackage.set(identity.callingPackage);
+ }
+
+ /**
* Change the authorities of the ContentProvider.
* This is normally set for you from its manifest information when the provider is first
* created.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c19909d..599c2d2 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -35,6 +35,12 @@
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.database.IContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -49,9 +55,11 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DocumentsContract;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.util.Size;
import com.android.internal.util.MimeIconUtils;
import com.android.internal.util.Preconditions;
@@ -68,6 +76,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -3188,4 +3197,62 @@
}
return query;
}
+
+ /**
+ * Convenience method that efficiently loads a visual thumbnail for the
+ * given {@link Uri}. Internally calls
+ * {@link ContentProvider#openTypedAssetFile} on the remote provider, but
+ * also defensively resizes any returned content to match the requested
+ * target size.
+ *
+ * @param uri The item that should be visualized as a thumbnail.
+ * @param size The target area on the screen where this thumbnail will be
+ * shown. This is passed to the provider as {@link #EXTRA_SIZE}
+ * to help it avoid downloading or generating heavy resources.
+ * @param signal A signal to cancel the operation in progress.
+ * @return Valid {@link Bitmap} which is a visual thumbnail.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
+ */
+ public @NonNull Bitmap loadThumbnail(@NonNull Uri uri, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ Objects.requireNonNull(uri);
+ Objects.requireNonNull(size);
+
+ try (ContentProviderClient client = acquireContentProviderClient(uri)) {
+ return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_DEFAULT);
+ }
+ }
+
+ /** {@hide} */
+ public static Bitmap loadThumbnail(@NonNull ContentProviderClient client, @NonNull Uri uri,
+ @NonNull Size size, @Nullable CancellationSignal signal, int allocator)
+ throws IOException {
+ Objects.requireNonNull(client);
+ Objects.requireNonNull(uri);
+ Objects.requireNonNull(size);
+
+ // Convert to Point, since that's what the API is defined as
+ final Bundle opts = new Bundle();
+ opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+ return client.openTypedAssetFileDescriptor(uri, "image/*", opts, signal);
+ }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
+ decoder.setAllocator(allocator);
+
+ // One last-ditch check to see if we've been canceled.
+ if (signal != null) signal.throwIfCanceled();
+
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final int widthSample = info.getSize().getWidth() / size.getWidth();
+ final int heightSample = info.getSize().getHeight() / size.getHeight();
+ final int sample = Math.min(widthSample, heightSample);
+ if (sample > 1) {
+ decoder.setTargetSampleSize(sample);
+ }
+ });
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index db4adcf..3197352 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4982,6 +4982,14 @@
public abstract Display getDisplay();
/**
+ * Gets the display ID.
+ *
+ * @return display ID associated with this {@link Context}.
+ * @hide
+ */
+ public abstract int getDisplayId();
+
+ /**
* @hide
*/
public abstract void updateDisplay(int displayId);
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c5dce017..bfad2b4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -921,6 +921,14 @@
* @hide
*/
@Override
+ public int getDisplayId() {
+ return mBase.getDisplayId();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public void updateDisplay(int displayId) {
mBase.updateDisplay(displayId);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8913748..c0463e9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4453,7 +4453,6 @@
*
* @hide
*/
- @SystemApi
public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";
/**
@@ -5009,8 +5008,7 @@
"android.intent.extra.user_handle";
/**
- * The UserHandle carried with broadcasts intents related to addition and removal of managed
- * profiles - {@link #ACTION_MANAGED_PROFILE_ADDED} and {@link #ACTION_MANAGED_PROFILE_REMOVED}.
+ * The UserHandle carried with intents.
*/
public static final String EXTRA_USER =
"android.intent.extra.USER";
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6a20c93..4a4de51 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -43,6 +43,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
@@ -273,7 +274,7 @@
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
- String dialogMessage, String callingPackage, int userId);
+ in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
boolean isPackageSuspendedForUser(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 00aa5c2..cdb7814 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -16,10 +16,12 @@
package android.content.pm;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_FIRST_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_SINGLE_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
+import static android.text.TextUtils.makeSafeForPresentation;
import android.annotation.FloatRange;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.res.XmlResourceParser;
@@ -27,17 +29,13 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.UserHandle;
-import android.text.Html;
-import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.Preconditions;
-import java.lang.annotation.Retention;
import java.text.Collator;
-import java.util.BitSet;
import java.util.Comparator;
/**
@@ -50,9 +48,6 @@
* in the implementation of Parcelable in subclasses.
*/
public class PackageItemInfo {
- private static final int LINE_FEED_CODE_POINT = 10;
- private static final int NBSP_CODE_POINT = 160;
-
/** The maximum length of a safe label, in characters */
private static final int MAX_SAFE_LABEL_LENGTH = 50000;
@@ -60,45 +55,43 @@
public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
/**
- * Flags for {@link #loadSafeLabel(PackageManager, float, int)}
- *
- * @hide
- */
- @Retention(SOURCE)
- @IntDef(flag = true, prefix = "SAFE_LABEL_FLAG_",
- value = {SAFE_LABEL_FLAG_TRIM, SAFE_LABEL_FLAG_SINGLE_LINE,
- SAFE_LABEL_FLAG_FIRST_LINE})
- public @interface SafeLabelFlags {}
-
- /**
* Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
* of the label.
*
* @see #loadSafeLabel(PackageManager, float, int)
+ *
+ * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead
* @hide
*/
+ @Deprecated
@SystemApi
- public static final int SAFE_LABEL_FLAG_TRIM = 0x1;
+ public static final int SAFE_LABEL_FLAG_TRIM = SAFE_STRING_FLAG_TRIM;
/**
* Force entire string into single line of text (no newlines). Cannot be set at the same time as
* {@link #SAFE_LABEL_FLAG_FIRST_LINE}.
*
* @see #loadSafeLabel(PackageManager, float, int)
+ *
+ * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead
* @hide
*/
+ @Deprecated
@SystemApi
- public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 0x2;
+ public static final int SAFE_LABEL_FLAG_SINGLE_LINE = SAFE_STRING_FLAG_SINGLE_LINE;
/**
* Return only first line of text (truncate at first newline). Cannot be set at the same time as
* {@link #SAFE_LABEL_FLAG_SINGLE_LINE}.
*
* @see #loadSafeLabel(PackageManager, float, int)
+ *
+ * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead
* @hide
*/
+ @Deprecated
@SystemApi
- public static final int SAFE_LABEL_FLAG_FIRST_LINE = 0x4;
+ public static final int SAFE_LABEL_FLAG_FIRST_LINE = SAFE_STRING_FLAG_FIRST_LINE;
private static volatile boolean sForceSafeLabels = false;
@@ -199,8 +192,8 @@
*/
public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
if (sForceSafeLabels) {
- return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_LABEL_FLAG_TRIM
- | SAFE_LABEL_FLAG_FIRST_LINE);
+ return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
+ | SAFE_STRING_FLAG_FIRST_LINE);
} else {
return loadUnsafeLabel(pm);
}
@@ -223,16 +216,6 @@
return packageName;
}
- private static boolean isNewline(int codePoint) {
- int type = Character.getType(codePoint);
- return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
- || codePoint == LINE_FEED_CODE_POINT;
- }
-
- private static boolean isWhiteSpace(int codePoint) {
- return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
- }
-
/**
* @hide
* @deprecated use loadSafeLabel(PackageManager, float, int) instead
@@ -240,209 +223,24 @@
@SystemApi
@Deprecated
public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
- return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_LABEL_FLAG_TRIM
- | SAFE_LABEL_FLAG_FIRST_LINE);
+ return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
+ | SAFE_STRING_FLAG_FIRST_LINE);
}
/**
- * A special string manipulation class. Just records removals and executes the when onString()
- * is called.
- */
- private static class StringWithRemovedChars {
- /** The original string */
- private final String mOriginal;
-
- /**
- * One bit per char in string. If bit is set, character needs to be removed. If whole
- * bit field is not initialized nothing needs to be removed.
- */
- private BitSet mRemovedChars;
-
- StringWithRemovedChars(@NonNull String original) {
- mOriginal = original;
- }
-
- /**
- * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including
- * firstNonRemoved) as removed.
- */
- void removeRange(int firstRemoved, int firstNonRemoved) {
- if (mRemovedChars == null) {
- mRemovedChars = new BitSet(mOriginal.length());
- }
-
- mRemovedChars.set(firstRemoved, firstNonRemoved);
- }
-
- /**
- * Remove all characters before {@code firstNonRemoved}.
- */
- void removeAllCharBefore(int firstNonRemoved) {
- if (mRemovedChars == null) {
- mRemovedChars = new BitSet(mOriginal.length());
- }
-
- mRemovedChars.set(0, firstNonRemoved);
- }
-
- /**
- * Remove all characters after and including {@code firstRemoved}.
- */
- void removeAllCharAfter(int firstRemoved) {
- if (mRemovedChars == null) {
- mRemovedChars = new BitSet(mOriginal.length());
- }
-
- mRemovedChars.set(firstRemoved, mOriginal.length());
- }
-
- @Override
- public String toString() {
- // Common case, no chars removed
- if (mRemovedChars == null) {
- return mOriginal;
- }
-
- StringBuilder sb = new StringBuilder(mOriginal.length());
- for (int i = 0; i < mOriginal.length(); i++) {
- if (!mRemovedChars.get(i)) {
- sb.append(mOriginal.charAt(i));
- }
- }
-
- return sb.toString();
- }
-
- /**
- * Return length or the original string
- */
- int length() {
- return mOriginal.length();
- }
-
- /**
- * Return if a certain {@code offset} of the original string is removed
- */
- boolean isRemoved(int offset) {
- return mRemovedChars != null && mRemovedChars.get(offset);
- }
-
- /**
- * Return codePoint of original string at a certain {@code offset}
- */
- int codePointAt(int offset) {
- return mOriginal.codePointAt(offset);
- }
- }
-
- /**
- * Load, clean up and truncate label before use.
+ * Calls {@link TextUtils#makeSafeForPresentation} for the label of this item.
*
- * <p>This method is meant to remove common mistakes and nefarious formatting from strings that
- * are used in sensitive parts of the UI.
+ * <p>For parameters see {@link TextUtils#makeSafeForPresentation}.
*
- * <p>This method first treats the string like HTML and then ...
- * <ul>
- * <li>Removes new lines or truncates at first new line
- * <li>Trims the white-space off the end
- * <li>Truncates the string to a given length
- * </ul>
- * ... if specified.
- *
- * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42.
- * This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br />
- * Usually ellipsizing should be left to the view showing the string. If a
- * string is used as an input to another string, it might be useful to
- * control the length of the input string though. {@code 0} disables this
- * feature.
- * @return The safe label
* @hide
- */
+ */
@SystemApi
public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm,
- @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags) {
- boolean onlyKeepFirstLine = ((flags & SAFE_LABEL_FLAG_FIRST_LINE) != 0);
- boolean forceSingleLine = ((flags & SAFE_LABEL_FLAG_SINGLE_LINE) != 0);
- boolean trim = ((flags & SAFE_LABEL_FLAG_TRIM) != 0);
-
+ @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags) {
Preconditions.checkNotNull(pm);
- Preconditions.checkArgument(ellipsizeDip >= 0);
- Preconditions.checkFlagsArgument(flags, SAFE_LABEL_FLAG_TRIM | SAFE_LABEL_FLAG_SINGLE_LINE
- | SAFE_LABEL_FLAG_FIRST_LINE);
- Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine),
- "Cannot set SAFE_LABEL_FLAG_SINGLE_LINE and SAFE_LABEL_FLAG_FIRST_LINE at the same "
- + "time");
- // loadLabel() always returns non-null
- String label = loadUnsafeLabel(pm).toString();
-
- // Treat string as HTML. This
- // - converts HTML symbols: e.g. ß -> ß
- // - applies some HTML tags: e.g. <br> -> \n
- // - removes invalid characters such as \b
- // - removes html styling, such as <b>
- // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc
- // - replaces some html tags by "object replacement" markers: <img> -> \ufffc
- // - Removes leading white space
- // - Removes all trailing white space beside a single space
- // - Collapses double white space
- StringWithRemovedChars labelStr = new StringWithRemovedChars(
- Html.fromHtml(label).toString());
-
- int firstNonWhiteSpace = -1;
- int firstTrailingWhiteSpace = -1;
-
- // Remove new lines (if requested) and control characters.
- int labelLength = labelStr.length();
- for (int offset = 0; offset < labelLength; ) {
- int codePoint = labelStr.codePointAt(offset);
- int type = Character.getType(codePoint);
- int codePointLen = Character.charCount(codePoint);
- boolean isNewline = isNewline(codePoint);
-
- if (offset > MAX_SAFE_LABEL_LENGTH || onlyKeepFirstLine && isNewline) {
- labelStr.removeAllCharAfter(offset);
- break;
- } else if (forceSingleLine && isNewline) {
- labelStr.removeRange(offset, offset + codePointLen);
- } else if (type == Character.CONTROL && !isNewline) {
- labelStr.removeRange(offset, offset + codePointLen);
- } else if (trim && !isWhiteSpace(codePoint)) {
- // This is only executed if the code point is not removed
- if (firstNonWhiteSpace == -1) {
- firstNonWhiteSpace = offset;
- }
- firstTrailingWhiteSpace = offset + codePointLen;
- }
-
- offset += codePointLen;
- }
-
- if (trim) {
- // Remove leading and trailing white space
- if (firstNonWhiteSpace == -1) {
- // No non whitespace found, remove all
- labelStr.removeAllCharAfter(0);
- } else {
- if (firstNonWhiteSpace > 0) {
- labelStr.removeAllCharBefore(firstNonWhiteSpace);
- }
- if (firstTrailingWhiteSpace < labelLength) {
- labelStr.removeAllCharAfter(firstTrailingWhiteSpace);
- }
- }
- }
-
- if (ellipsizeDip == 0) {
- return labelStr.toString();
- } else {
- // Truncate
- final TextPaint paint = new TextPaint();
- paint.setTextSize(42);
-
- return TextUtils.ellipsize(labelStr.toString(), paint, ellipsizeDip,
- TextUtils.TruncateAt.END);
- }
+ return makeSafeForPresentation(loadUnsafeLabel(pm).toString(), MAX_SAFE_LABEL_LENGTH,
+ ellipsizeDip, flags);
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 733fbe5..dfb8128 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5664,7 +5664,7 @@
* {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
*
* @param packageNames The names of the packages to set the suspended status.
- * @param suspended If set to {@code true} than the packages will be suspended, if set to
+ * @param suspended If set to {@code true}, the packages will be suspended, if set to
* {@code false}, the packages will be unsuspended.
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
* which will be shared with the apps being suspended. Ignored if
@@ -5676,15 +5676,76 @@
* suspended app.
*
* @return an array of package names for which the suspended status could not be set as
- * requested in this method.
+ * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+ *
+ * @deprecated use {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, android.content.pm.SuspendDialogInfo)} instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+ @Nullable
+ public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+ @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+ @Nullable String dialogMessage) {
+ throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+ }
+
+ /**
+ * Puts the given packages in a suspended state, where attempts at starting activities are
+ * denied.
+ *
+ * <p>The suspended application's notifications and all of its windows will be hidden, any
+ * of its started activities will be stopped and it won't be able to ring the device.
+ * It doesn't remove the data or the actual package file.
+ *
+ * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
+ * is suspended will be shown instead.
+ * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
+ * to this api. This dialog will have a button that starts the
+ * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
+ * activity which handles this action.
+ *
+ * <p>The packages being suspended must already be installed. If a package is uninstalled, it
+ * will no longer be suspended.
+ *
+ * <p>Optionally, the suspending app can provide extra information in the form of
+ * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+ * launcher to support customization that they might need to handle the suspended state.
+ *
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
+ *
+ * @param packageNames The names of the packages to set the suspended status.
+ * @param suspended If set to {@code true}, the packages will be suspended, if set to
+ * {@code false}, the packages will be unsuspended.
+ * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+ * which will be shared with the apps being suspended. Ignored if
+ * {@code suspended} is false.
+ * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+ * provide which will be shared with the launcher. Ignored if
+ * {@code suspended} is false.
+ * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
+ * should be shown to the user when they try to launch a suspended app.
+ * Ignored if {@code suspended} is false.
+ *
+ * @return an array of package names for which the suspended status could not be set as
+ * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+ *
+ * @see #isPackageSuspended
+ * @see SuspendDialogInfo
+ * @see SuspendDialogInfo.Builder
+ * @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
- public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ @Nullable
+ public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
- String dialogMessage) {
+ @Nullable SuspendDialogInfo dialogInfo) {
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 7b4c6fc..4f58321 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -267,14 +267,15 @@
public abstract String getSuspendingPackage(String suspendedPackage, int userId);
/**
- * Get the dialog message to be shown to the user when they try to launch a suspended
- * application.
+ * Get the information describing the dialog to be shown to the user when they try to launch a
+ * suspended application.
*
* @param suspendedPackage The package that has been suspended.
* @param userId The user for which to check.
- * @return The dialog message to be shown to the user.
+ * @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
*/
- public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
+ @Nullable
+ public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
/**
* Do a straight uid lookup for the given package/application in the given user.
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 248d523..e21c33a 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -33,6 +33,7 @@
import android.os.PersistableBundle;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import java.util.Arrays;
@@ -50,7 +51,7 @@
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
public String suspendingPackage;
- public String dialogMessage; // Message to show when a suspended package launch attempt is made
+ public SuspendDialogInfo dialogInfo;
public PersistableBundle suspendedAppExtras;
public PersistableBundle suspendedLauncherExtras;
public boolean instantApp;
@@ -79,6 +80,7 @@
installReason = PackageManager.INSTALL_REASON_UNKNOWN;
}
+ @VisibleForTesting
public PackageUserState(PackageUserState o) {
ceDataInode = o.ceDataInode;
installed = o.installed;
@@ -87,7 +89,7 @@
hidden = o.hidden;
suspended = o.suspended;
suspendingPackage = o.suspendingPackage;
- dialogMessage = o.dialogMessage;
+ dialogInfo = o.dialogInfo;
suspendedAppExtras = o.suspendedAppExtras;
suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp;
@@ -217,7 +219,7 @@
|| !suspendingPackage.equals(oldState.suspendingPackage)) {
return false;
}
- if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
+ if (!Objects.equals(dialogInfo, oldState.dialogInfo)) {
return false;
}
if (!BaseBundle.kindofEquals(suspendedAppExtras,
diff --git a/core/java/android/content/pm/SuspendDialogInfo.aidl b/core/java/android/content/pm/SuspendDialogInfo.aidl
new file mode 100644
index 0000000..5e711cf
--- /dev/null
+++ b/core/java/android/content/pm/SuspendDialogInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable SuspendDialogInfo;
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
new file mode 100644
index 0000000..c798c99
--- /dev/null
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.res.ResourceId.ID_NULL;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.content.res.ResourceId;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A container to describe the dialog to be shown when the user tries to launch a suspended
+ * application.
+ * The suspending app can customize the dialog's following attributes:
+ * <ul>
+ * <li>The dialog icon, by providing a resource id.
+ * <li>The title text, by providing a resource id.
+ * <li>The text of the dialog's body, by providing a resource id or a string.
+ * <li>The text on the neutral button which starts the
+ * {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS SHOW_SUSPENDED_APP_DETAILS}
+ * activity, by providing a resource id.
+ * </ul>
+ * System defaults are used whenever any of these are not provided, or any of the provided resource
+ * ids cannot be resolved at the time of displaying the dialog.
+ *
+ * @hide
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * SuspendDialogInfo)
+ * @see Builder
+ */
+@SystemApi
+public final class SuspendDialogInfo implements Parcelable {
+ private static final String TAG = SuspendDialogInfo.class.getSimpleName();
+ private static final String XML_ATTR_ICON_RES_ID = "iconResId";
+ private static final String XML_ATTR_TITLE_RES_ID = "titleResId";
+ private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
+ private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
+ private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
+
+ private final int mIconResId;
+ private final int mTitleResId;
+ private final int mDialogMessageResId;
+ private final String mDialogMessage;
+ private final int mNeutralButtonTextResId;
+
+ /**
+ * @return the resource id of the icon to be used with the dialog
+ * @hide
+ */
+ @DrawableRes
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ /**
+ * @return the resource id of the title to be used with the dialog
+ * @hide
+ */
+ @StringRes
+ public int getTitleResId() {
+ return mTitleResId;
+ }
+
+ /**
+ * @return the resource id of the text to be shown in the dialog's body
+ * @hide
+ */
+ @StringRes
+ public int getDialogMessageResId() {
+ return mDialogMessageResId;
+ }
+
+ /**
+ * @return the text to be shown in the dialog's body. Returns {@code null} if
+ * {@link #getDialogMessageResId()} returns a valid resource id.
+ * @hide
+ */
+ @Nullable
+ public String getDialogMessage() {
+ return mDialogMessage;
+ }
+
+ /**
+ * @return the text to be shown
+ * @hide
+ */
+ @StringRes
+ public int getNeutralButtonTextResId() {
+ return mNeutralButtonTextResId;
+ }
+
+ /**
+ * @hide
+ */
+ public void saveToXml(XmlSerializer out) throws IOException {
+ if (mIconResId != ID_NULL) {
+ XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId);
+ }
+ if (mTitleResId != ID_NULL) {
+ XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId);
+ }
+ if (mDialogMessageResId != ID_NULL) {
+ XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
+ } else {
+ XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage);
+ }
+ if (mNeutralButtonTextResId != ID_NULL) {
+ XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static SuspendDialogInfo restoreFromXml(XmlPullParser in) {
+ final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder();
+ try {
+ final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL);
+ final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
+ final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
+ ID_NULL);
+ final int dialogMessageResId = XmlUtils.readIntAttribute(
+ in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
+ final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
+
+ if (iconId != ID_NULL) {
+ dialogInfoBuilder.setIcon(iconId);
+ }
+ if (titleId != ID_NULL) {
+ dialogInfoBuilder.setTitle(titleId);
+ }
+ if (buttonTextId != ID_NULL) {
+ dialogInfoBuilder.setNeutralButtonText(buttonTextId);
+ }
+ if (dialogMessageResId != ID_NULL) {
+ dialogInfoBuilder.setMessage(dialogMessageResId);
+ } else if (dialogMessage != null) {
+ dialogInfoBuilder.setMessage(dialogMessage);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while parsing from xml. Some fields may default", e);
+ }
+ return dialogInfoBuilder.build();
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = mIconResId;
+ hashCode = 31 * hashCode + mTitleResId;
+ hashCode = 31 * hashCode + mNeutralButtonTextResId;
+ hashCode = 31 * hashCode + mDialogMessageResId;
+ hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof SuspendDialogInfo)) {
+ return false;
+ }
+ final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
+ return mIconResId == otherDialogInfo.mIconResId
+ && mTitleResId == otherDialogInfo.mTitleResId
+ && mDialogMessageResId == otherDialogInfo.mDialogMessageResId
+ && mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
+ && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder("SuspendDialogInfo: {");
+ if (mIconResId != ID_NULL) {
+ builder.append("mIconId = 0x");
+ builder.append(Integer.toHexString(mIconResId));
+ builder.append(" ");
+ }
+ if (mTitleResId != ID_NULL) {
+ builder.append("mTitleResId = 0x");
+ builder.append(Integer.toHexString(mTitleResId));
+ builder.append(" ");
+ }
+ if (mNeutralButtonTextResId != ID_NULL) {
+ builder.append("mNeutralButtonTextResId = 0x");
+ builder.append(Integer.toHexString(mNeutralButtonTextResId));
+ builder.append(" ");
+ }
+ if (mDialogMessageResId != ID_NULL) {
+ builder.append("mDialogMessageResId = 0x");
+ builder.append(Integer.toHexString(mDialogMessageResId));
+ builder.append(" ");
+ } else if (mDialogMessage != null) {
+ builder.append("mDialogMessage = \"");
+ builder.append(mDialogMessage);
+ builder.append("\" ");
+ }
+ builder.append("}");
+ return builder.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeInt(mIconResId);
+ dest.writeInt(mTitleResId);
+ dest.writeInt(mDialogMessageResId);
+ dest.writeString(mDialogMessage);
+ dest.writeInt(mNeutralButtonTextResId);
+ }
+
+ private SuspendDialogInfo(Parcel source) {
+ mIconResId = source.readInt();
+ mTitleResId = source.readInt();
+ mDialogMessageResId = source.readInt();
+ mDialogMessage = source.readString();
+ mNeutralButtonTextResId = source.readInt();
+ }
+
+ SuspendDialogInfo(Builder b) {
+ mIconResId = b.mIconResId;
+ mTitleResId = b.mTitleResId;
+ mDialogMessageResId = b.mDialogMessageResId;
+ mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
+ mNeutralButtonTextResId = b.mNeutralButtonTextResId;
+ }
+
+ public static final Creator<SuspendDialogInfo> CREATOR = new Creator<SuspendDialogInfo>() {
+ @Override
+ public SuspendDialogInfo createFromParcel(Parcel source) {
+ return new SuspendDialogInfo(source);
+ }
+
+ @Override
+ public SuspendDialogInfo[] newArray(int size) {
+ return new SuspendDialogInfo[size];
+ }
+ };
+
+ /**
+ * Builder to build a {@link SuspendDialogInfo} object.
+ */
+ public static final class Builder {
+ private int mDialogMessageResId = ID_NULL;
+ private String mDialogMessage;
+ private int mTitleResId = ID_NULL;
+ private int mIconResId = ID_NULL;
+ private int mNeutralButtonTextResId = ID_NULL;
+
+ /**
+ * Set the resource id of the icon to be used. If not provided, no icon will be shown.
+ *
+ * @param resId The resource id of the icon.
+ * @return this builder object.
+ */
+ @NonNull
+ public Builder setIcon(@DrawableRes int resId) {
+ Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+ mIconResId = resId;
+ return this;
+ }
+
+ /**
+ * Set the resource id of the title text to be displayed. If this is not provided, the
+ * system will use a default title.
+ *
+ * @param resId The resource id of the title.
+ * @return this builder object.
+ */
+ @NonNull
+ public Builder setTitle(@StringRes int resId) {
+ Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+ mTitleResId = resId;
+ return this;
+ }
+
+ /**
+ * Set the text to show in the body of the dialog. Ignored if a resource id is set via
+ * {@link #setMessage(int)}.
+ * <p>
+ * The system will use {@link String#format(Locale, String, Object...) String.format} to
+ * insert the suspended app name into the message, so an example format string could be
+ * {@code "The app %1$s is currently suspended"}. This is optional - if the string passed in
+ * {@code message} does not accept an argument, it will be used as is.
+ *
+ * @param message The dialog message.
+ * @return this builder object.
+ * @see #setMessage(int)
+ */
+ @NonNull
+ public Builder setMessage(@NonNull String message) {
+ Preconditions.checkStringNotEmpty(message, "Message cannot be null or empty");
+ mDialogMessage = message;
+ return this;
+ }
+
+ /**
+ * Set the resource id of the dialog message to be shown. If no dialog message is provided
+ * via either this method or {@link #setMessage(String)}, the system will use a
+ * default message.
+ * <p>
+ * The system will use {@link android.content.res.Resources#getString(int, Object...)
+ * getString} to insert the suspended app name into the message, so an example format string
+ * could be {@code "The app %1$s is currently suspended"}. This is optional - if the string
+ * referred to by {@code resId} does not accept an argument, it will be used as is.
+ *
+ * @param resId The resource id of the dialog message.
+ * @return this builder object.
+ * @see #setMessage(String)
+ */
+ @NonNull
+ public Builder setMessage(@StringRes int resId) {
+ Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+ mDialogMessageResId = resId;
+ return this;
+ }
+
+ /**
+ * Set the resource id of text to be shown on the neutral button. Tapping this button starts
+ * the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity. If this is
+ * not provided, the system will use a default text.
+ *
+ * @param resId The resource id of the button text
+ * @return this builder object.
+ */
+ @NonNull
+ public Builder setNeutralButtonText(@StringRes int resId) {
+ Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+ mNeutralButtonTextResId = resId;
+ return this;
+ }
+
+ /**
+ * Build the final object based on given inputs.
+ *
+ * @return The {@link SuspendDialogInfo} object built using this builder.
+ */
+ @NonNull
+ public SuspendDialogInfo build() {
+ return new SuspendDialogInfo(this);
+ }
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 09113e5..01ef58e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -397,7 +397,7 @@
if (display == null) {
// TODO: We cannot currently provide any override configurations for metrics on displays
// other than the display the context is associated with.
- final Context context = mContext.getDisplay().getDisplayId() == displayId
+ final Context context = mContext.getDisplayId() == displayId
? mContext : mContext.getApplicationContext();
display = mGlobal.getCompatibleDisplay(displayId, context.getResources());
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 6609b76..5de89e3 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -105,12 +105,15 @@
*
* This method should be used if the caller wants to receive notifications even after the
* process exits. The client must have an open connection with the Context Hub Service (i.e. it
- * cannot have been closed through the {@link #close()} method). If registered successfully,
- * intents will be delivered regarding events for the specified nanoapp from the attached
- * Context Hub. Any unicast messages for this client will also be delivered. The intent will
- * have an extra {@link #EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which will
- * contain the type of the event. See {@link ContextHubManager.Event} for description of each
- * event type.
+ * cannot have been closed through the {@link #close()} method). Only one PendingIntent can be
+ * registered at a time for a single ContextHubClient. If registered successfully, intents will
+ * be delivered regarding events for the specified nanoapp from the attached Context Hub. Any
+ * unicast messages for this client will also be delivered. The intent will have an extra
+ * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
+ * describes the Context Hub the intent event was for. The intent will also have an extra
+ * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
+ * will contain the type of the event. See {@link ContextHubManager.Event} for description of
+ * each event type, along with event-specific extra fields.
*
* When the intent is received, this client can be recreated through
* {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c496ff4..4714587 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2731,7 +2731,10 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK})
@SystemApi
public void setAirplaneMode(boolean enable) {
try {
@@ -2813,10 +2816,11 @@
* @param network The {@link Network} of the satisfying network.
* @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
* @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide
*/
public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
- LinkProperties linkProperties) {
+ LinkProperties linkProperties, boolean blocked) {
// Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior.
@@ -2827,6 +2831,7 @@
}
onCapabilitiesChanged(network, networkCapabilities);
onLinkPropertiesChanged(network, linkProperties);
+ onBlockedStatusChanged(network, blocked);
}
/**
@@ -2834,7 +2839,8 @@
* This callback may be called more than once if the {@link Network} that is
* satisfying the request changes. This will always immediately be followed by a
* call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
- * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+ * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
+ * {@link #onBlockedStatusChanged(Network, boolean)}.
*
* @param network The {@link Network} of the satisfying network.
*/
@@ -2913,6 +2919,14 @@
*/
public void onNetworkResumed(Network network) {}
+ /**
+ * Called when access to the specified network is blocked or unblocked.
+ *
+ * @param network The {@link Network} whose blocked status has changed.
+ * @param blocked The blocked status of this {@link Network}.
+ */
+ public void onBlockedStatusChanged(Network network, boolean blocked) {}
+
private NetworkRequest networkRequest;
}
@@ -2959,6 +2973,8 @@
public static final int CALLBACK_SUSPENDED = BASE + 9;
/** @hide */
public static final int CALLBACK_RESUMED = BASE + 10;
+ /** @hide */
+ public static final int CALLBACK_BLK_CHANGED = BASE + 11;
/** @hide */
public static String getCallbackName(int whichCallback) {
@@ -2973,6 +2989,7 @@
case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED";
case CALLBACK_RESUMED: return "CALLBACK_RESUMED";
+ case CALLBACK_BLK_CHANGED: return "CALLBACK_BLK_CHANGED";
default:
return Integer.toString(whichCallback);
}
@@ -3019,7 +3036,7 @@
case CALLBACK_AVAILABLE: {
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
LinkProperties lp = getObject(message, LinkProperties.class);
- callback.onAvailable(network, cap, lp);
+ callback.onAvailable(network, cap, lp, message.arg1 != 0);
break;
}
case CALLBACK_LOSING: {
@@ -3052,6 +3069,10 @@
callback.onNetworkResumed(network);
break;
}
+ case CALLBACK_BLK_CHANGED: {
+ boolean blocked = message.arg1 != 0;
+ callback.onBlockedStatusChanged(network, blocked);
+ }
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 12b6f9e..0bdfca7 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1590,4 +1590,14 @@
Preconditions.checkArgument(isValidCapability(capability),
"NetworkCapability " + capability + "out of range");
}
+
+ /**
+ * Check if this {@code NetworkCapability} instance is metered.
+ *
+ * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+ * @hide
+ */
+ public boolean isMetered() {
+ return !hasCapability(NET_CAPABILITY_NOT_METERED);
+ }
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index d912dd10..1a1d2d334 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -202,7 +202,9 @@
* Return a network-type-specific integer describing the subtype
* of the network.
* @return the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
*/
+ @Deprecated
public int getSubtype() {
synchronized (this) {
return mSubtype;
@@ -243,7 +245,9 @@
/**
* Return a human-readable name describing the subtype of the network.
* @return the name of the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
*/
+ @Deprecated
public String getSubtypeName() {
synchronized (this) {
return mSubtypeName;
@@ -278,7 +282,15 @@
* connections and pass data.
* <p>Always call this before attempting to perform data transactions.
* @return {@code true} if network connectivity exists, {@code false} otherwise.
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
*/
+ @Deprecated
public boolean isConnected() {
synchronized (this) {
return mState == State.CONNECTED;
@@ -411,7 +423,15 @@
/**
* Reports the current fine-grained state of the network.
* @return the fine-grained state
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
*/
+ @Deprecated
public DetailedState getDetailedState() {
synchronized (this) {
return mDetailedState;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 34e9476..c431e40e 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,8 +16,13 @@
package android.net;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
+import android.system.Os;
import android.util.Log;
import android.util.Pair;
@@ -570,4 +575,30 @@
}
return routedIPCount;
}
+
+ private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6};
+
+ /**
+ * Returns true if the hostname is weakly validated.
+ * @param hostname Name of host to validate.
+ * @return True if it's a valid-ish hostname.
+ *
+ * @hide
+ */
+ public static boolean isWeaklyValidatedHostname(@NonNull String hostname) {
+ // TODO(b/34953048): Use a validation method that permits more accurate,
+ // but still inexpensive, checking of likely valid DNS hostnames.
+ final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$";
+ if (!hostname.matches(weakHostnameRegex)) {
+ return false;
+ }
+
+ for (int address_family : ADDRESS_FAMILIES) {
+ if (Os.inet_pton(address_family, hostname) != null) {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 40465ce..d09f33b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1102,19 +1102,18 @@
public String getHost() {
@SuppressWarnings("StringEquality")
boolean cached = (host != NOT_CACHED);
- return cached ? host
- : (host = parseHost());
+ return cached ? host : (host = parseHost());
}
private String parseHost() {
- String authority = getEncodedAuthority();
+ final String authority = getEncodedAuthority();
if (authority == null) {
return null;
}
// Parse out user info and then port.
int userInfoSeparator = authority.lastIndexOf('@');
- int portSeparator = authority.indexOf(':', userInfoSeparator);
+ int portSeparator = findPortSeparator(authority);
String encodedHost = portSeparator == NOT_FOUND
? authority.substring(userInfoSeparator + 1)
@@ -1132,16 +1131,8 @@
}
private int parsePort() {
- String authority = getEncodedAuthority();
- if (authority == null) {
- return -1;
- }
-
- // Make sure we look for the port separtor *after* the user info
- // separator. We have URLs with a ':' in the user info.
- int userInfoSeparator = authority.lastIndexOf('@');
- int portSeparator = authority.indexOf(':', userInfoSeparator);
-
+ final String authority = getEncodedAuthority();
+ int portSeparator = findPortSeparator(authority);
if (portSeparator == NOT_FOUND) {
return -1;
}
@@ -1154,6 +1145,24 @@
return -1;
}
}
+
+ private int findPortSeparator(String authority) {
+ if (authority == null) {
+ return NOT_FOUND;
+ }
+
+ // Reverse search for the ':' character that breaks as soon as a char that is neither
+ // a colon nor an ascii digit is encountered. Thanks to the goodness of UTF-16 encoding,
+ // it's not possible that a surrogate matches one of these, so this loop can just
+ // look for characters rather than care about code points.
+ for (int i = authority.length() - 1; i >= 0; --i) {
+ final int character = authority.charAt(i);
+ if (':' == character) return i;
+ // Character.isDigit would include non-ascii digits
+ if (character < '0' || character > '9') return NOT_FOUND;
+ }
+ return NOT_FOUND;
+ }
}
/**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 412a700..292543c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -130,9 +130,9 @@
* <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain
* proof of the device's original identifiers.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. Profile owner access is
+ * deprecated and will be removed in a future release.
*
* @return The serial number if specified.
*/
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index a9cb0d9..f71fdd7 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1198,6 +1198,19 @@
/** {@hide} */
public static int translateModeStringToPosix(String mode) {
+ // Sanity check for invalid chars
+ for (int i = 0; i < mode.length(); i++) {
+ switch (mode.charAt(i)) {
+ case 'r':
+ case 'w':
+ case 't':
+ case 'a':
+ break;
+ default:
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ }
+
int res = 0;
if (mode.startsWith("rw")) {
res |= O_RDWR | O_CREAT;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 5f65620..0c56d48 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
import android.opengl.EGL14;
import android.os.Build;
import android.os.SystemProperties;
@@ -27,7 +29,14 @@
import dalvik.system.VMRuntime;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashSet;
+import java.util.Set;
/** @hide */
public class GraphicsEnvironment {
@@ -44,8 +53,10 @@
private static final boolean DEBUG = false;
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+ private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
+ private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -55,7 +66,7 @@
* Set up GraphicsEnvironment
*/
public void setup(Context context, Bundle coreSettings) {
- setupGpuLayers(context);
+ setupGpuLayers(context, coreSettings);
setupAngle(context, coreSettings);
chooseDriver(context);
}
@@ -81,27 +92,54 @@
}
/**
+ * Return the debug layer app's on-disk and in-APK lib directories
+ */
+ private static String getDebugLayerAppPaths(Context context, String app) {
+ ApplicationInfo appInfo;
+ try {
+ appInfo = context.getPackageManager().getApplicationInfo(
+ app, PackageManager.MATCH_ALL);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Debug layer app '" + app + "' not installed");
+
+ return null;
+ }
+
+ String abi = chooseAbi(appInfo);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(appInfo.nativeLibraryDir)
+ .append(File.pathSeparator);
+ sb.append(appInfo.sourceDir)
+ .append("!/lib/")
+ .append(abi);
+ String paths = sb.toString();
+
+ if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths);
+
+ return paths;
+ }
+
+ /**
* Set up layer search paths for all apps
* If debuggable, check for additional debug settings
*/
- private void setupGpuLayers(Context context) {
+ private void setupGpuLayers(Context context, Bundle coreSettings) {
String layerPaths = "";
// Only enable additional debug functionality if the following conditions are met:
- // 1. App is debuggable
+ // 1. App is debuggable or device is rooted
// 2. ENABLE_GPU_DEBUG_LAYERS is true
// 3. Package name is equal to GPU_DEBUG_APP
- if (isDebuggable(context)) {
+ if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
- int enable = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+ int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
if (enable != 0) {
- String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.GPU_DEBUG_APP);
+ String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP);
String packageName = context.getPackageName();
@@ -115,8 +153,22 @@
// the layers specified by the app.
layerPaths = mDebugLayerPath + ":";
- String layers = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.GPU_DEBUG_LAYERS);
+
+ // If there is a debug layer app specified, add its path.
+ String gpuDebugLayerApp =
+ coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP);
+
+ if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) {
+ Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp);
+ String paths = getDebugLayerAppPaths(context, gpuDebugLayerApp);
+ if (paths != null) {
+ // Append the path so files placed in the app's base directory will
+ // override the external path
+ layerPaths += paths + ":";
+ }
+ }
+
+ String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
Log.i(TAG, "Debug layer list: " + layers);
if (layers != null && !layers.isEmpty()) {
@@ -201,8 +253,40 @@
if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
+ // Pass the rules file to loader for ANGLE decisions
+ AssetManager angleAssets = null;
+ try {
+ angleAssets =
+ context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
+ return;
+ }
+
+ AssetFileDescriptor assetsFd = null;
+ try {
+ assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
+ + "'" + ANGLE_PACKAGE_NAME + "'");
+ return;
+ }
+
+ FileDescriptor rulesFd = null;
+ long rulesOffset = 0;
+ long rulesLength = 0;
+ if (assetsFd != null) {
+ rulesFd = assetsFd.getFileDescriptor();
+ rulesOffset = assetsFd.getStartOffset();
+ rulesLength = assetsFd.getLength();
+ } else {
+ Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
+ return;
+ }
+
// Further opt-in logic is handled in native, so pass relevant info down
- setAngleInfo(paths, packageName, appPref, devOptIn);
+ setAngleInfo(paths, packageName, appPref, devOptIn,
+ rulesFd, rulesOffset, rulesLength);
}
/**
@@ -221,6 +305,15 @@
if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
return;
}
+ Set<String> whitelist = loadWhitelist(context, driverPackageName);
+
+ // Empty whitelist implies no updatable graphics driver. Typically, the pre-installed
+ // updatable graphics driver is supposed to be a place holder and contains no graphics
+ // driver and whitelist.
+ if (whitelist == null || whitelist.isEmpty()) {
+ return;
+ }
+
ApplicationInfo driverInfo;
try {
driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
@@ -229,6 +322,22 @@
Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
return;
}
+ if (!whitelist.contains(context.getPackageName())) {
+ if (DEBUG) {
+ Log.w(TAG, context.getPackageName() + " is not on the whitelist.");
+ }
+ return;
+ }
+
+ // O drivers are restricted to the sphal linker namespace, so don't try to use
+ // packages unless they declare they're compatible with that restriction.
+ if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ if (DEBUG) {
+ Log.w(TAG, "updated driver package is not known to be compatible with O");
+ }
+ return;
+ }
+
String abi = chooseAbi(driverInfo);
if (abi == null) {
if (DEBUG) {
@@ -239,12 +348,6 @@
}
return;
}
- if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
- // O drivers are restricted to the sphal linker namespace, so don't try to use
- // packages unless they declare they're compatible with that restriction.
- Log.w(TAG, "updated driver package is not known to be compatible with O");
- return;
- }
StringBuilder sb = new StringBuilder();
sb.append(driverInfo.nativeLibraryDir)
@@ -290,9 +393,39 @@
return null;
}
+ private static Set<String> loadWhitelist(Context context, String driverPackageName) {
+ String whitelistName = SystemProperties.get(PROPERTY_GFX_DRIVER_WHITELIST);
+ if (whitelistName == null || whitelistName.isEmpty()) {
+ return null;
+ }
+ try {
+ Context driverContext = context.createPackageContext(driverPackageName,
+ Context.CONTEXT_RESTRICTED);
+ AssetManager assets = driverContext.getAssets();
+ InputStream stream = assets.open(whitelistName);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ Set<String> whitelist = new HashSet<>();
+ for (String line; (line = reader.readLine()) != null; ) {
+ whitelist.add(line);
+ }
+ return whitelist;
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
+ }
+ } catch (IOException e) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to load whitelist driver package, abort.");
+ }
+ }
+ return null;
+ }
+
+ private static native int getCanLoadSystemLibraries();
private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
private static native void setDebugLayers(String layers);
private static native void setDriverPath(String path);
private static native void setAngleInfo(String path, String appPackage, String appPref,
- boolean devOptIn);
+ boolean devOptIn, FileDescriptor rulesFd,
+ long rulesOffset, long rulesLength);
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 20ca19b..c9c4205 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -388,10 +388,10 @@
/**
* Setup a new physical network.
- * @param permission null if no permissions required to access this network. PERMISSION_NETWORK
- * or PERMISSION_SYSTEM to set respective permission.
+ * @param permission PERMISSION_NONE if no permissions required to access this network.
+ * PERMISSION_NETWORK or PERMISSION_SYSTEM to set respective permission.
*/
- void createPhysicalNetwork(int netId, String permission);
+ void createPhysicalNetwork(int netId, int permission);
/**
* Setup a new VPN.
@@ -420,10 +420,10 @@
/**
* Set permission for a network.
- * @param permission null to clear permissions. PERMISSION_NETWORK or PERMISSION_SYSTEM to set
- * permission.
+ * @param permission PERMISSION_NONE to clear permissions.
+ * PERMISSION_NETWORK or PERMISSION_SYSTEM to set permission.
*/
- void setNetworkPermission(int netId, String permission);
+ void setNetworkPermission(int netId, int permission);
void setPermission(String permission, in int[] uids);
void clearPermission(in int[] uids);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8123744..00b989e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -272,6 +272,10 @@
*
* Specifies if all users on the device are disallowed from enabling the
* "Unknown Sources" setting, that allows installation of apps from unknown sources.
+ *
+ * This restriction can be enabled by the profile owner, in which case all accounts and
+ * profiles will be affected.
+ *
* The default value is <code>false</code>.
*
* <p>Key for user restrictions.
@@ -983,6 +987,21 @@
public static final String DISALLOW_PRINTING = "no_printing";
/**
+ * Specifies whether the user is allowed to modify private DNS settings.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>This user restriction can only be applied by the Device Owner.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_PRIVATE_DNS =
+ "disallow_config_private_dns";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -2363,7 +2382,6 @@
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS}, conditional = true)
- @SystemApi
public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
try {
return mService.getProfileIds(userId, enabledOnly);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 50ca4ab..df771df 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1538,6 +1538,9 @@
* @hide
*/
public File translateAppToSystem(File file, String packageName) {
+ // We can only translate absolute paths
+ if (!file.isAbsolute()) return file;
+
try {
return new File(mStorageManager.translateAppToSystem(file.getAbsolutePath(),
packageName, mContext.getUserId()));
@@ -1553,6 +1556,9 @@
* @hide
*/
public File translateSystemToApp(File file, String packageName) {
+ // We can only translate absolute paths
+ if (!file.isAbsolute()) return file;
+
try {
return new File(mStorageManager.translateSystemToApp(file.getAbsolutePath(),
packageName, mContext.getUserId()));
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8c40e0e..954d18a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -34,6 +34,7 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.graphics.Point;
import android.media.ExifInterface;
@@ -53,6 +54,7 @@
import android.system.Os;
import android.util.DataUnit;
import android.util.Log;
+import android.util.Size;
import libcore.io.IoUtils;
@@ -136,10 +138,11 @@
public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
/**
- * Included in {@link AssetFileDescriptor#getExtras()} when returned
- * thumbnail should be rotated.
+ * An extra number of degrees that an image should be rotated during the
+ * decode process to be presented correctly.
*
- * @see MediaStore.Images.ImageColumns#ORIENTATION
+ * @see AssetFileDescriptor#getExtras()
+ * @see android.provider.MediaStore.Images.ImageColumns#ORIENTATION
*/
public static final String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
@@ -1093,75 +1096,10 @@
/** {@hide} */
@UnsupportedAppUsage
- public static Bitmap getDocumentThumbnail(
- ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
- throws RemoteException, IOException {
- final Bundle openOpts = new Bundle();
- openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size);
-
- AssetFileDescriptor afd = null;
- Bitmap bitmap = null;
- try {
- afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
-
- final FileDescriptor fd = afd.getFileDescriptor();
- final long offset = afd.getStartOffset();
-
- // Try seeking on the returned FD, since it gives us the most
- // optimal decode path; otherwise fall back to buffering.
- BufferedInputStream is = null;
- try {
- Os.lseek(fd, offset, SEEK_SET);
- } catch (ErrnoException e) {
- is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE);
- is.mark(THUMBNAIL_BUFFER_SIZE);
- }
-
- // We requested a rough thumbnail size, but the remote size may have
- // returned something giant, so defensively scale down as needed.
- final BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inJustDecodeBounds = true;
- if (is != null) {
- BitmapFactory.decodeStream(is, null, opts);
- } else {
- BitmapFactory.decodeFileDescriptor(fd, null, opts);
- }
-
- final int widthSample = opts.outWidth / size.x;
- final int heightSample = opts.outHeight / size.y;
-
- opts.inJustDecodeBounds = false;
- opts.inSampleSize = Math.min(widthSample, heightSample);
- if (is != null) {
- is.reset();
- bitmap = BitmapFactory.decodeStream(is, null, opts);
- } else {
- try {
- Os.lseek(fd, offset, SEEK_SET);
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
- }
- bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts);
- }
-
- // Transform the bitmap if requested. We use a side-channel to
- // communicate the orientation, since EXIF thumbnails don't contain
- // the rotation flags of the original image.
- final Bundle extras = afd.getExtras();
- final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
- if (orientation != 0) {
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
-
- final Matrix m = new Matrix();
- m.setRotate(orientation, width / 2, height / 2);
- bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
- }
- } finally {
- IoUtils.closeQuietly(afd);
- }
-
- return bitmap;
+ public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
+ Point size, CancellationSignal signal) throws IOException {
+ return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
+ ImageDecoder.ALLOCATOR_DEFAULT);
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 828fd73..f5660b9 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -29,7 +29,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
-import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Bitmap;
@@ -39,6 +38,7 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
+import android.os.OperationCanceledException;
import android.os.RemoteException;
import android.service.media.CameraPrewarmService;
import android.util.ArrayMap;
@@ -402,7 +402,16 @@
* access.
* <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access. This value will always be {@code NULL}
+ * for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
@@ -641,6 +650,7 @@
* This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
* to be accessed elsewhere.
*/
+ @Deprecated
private static class InternalThumbnails implements BaseColumns {
/**
* Currently outstanding thumbnail requests that can be cancelled.
@@ -654,13 +664,14 @@
*
* @see #cancelThumbnail(ContentResolver, Uri)
*/
+ @Deprecated
static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
int kind, @Nullable BitmapFactory.Options opts) {
- final Bundle openOpts = new Bundle();
+ final Point size;
if (kind == ThumbnailConstants.MICRO_KIND) {
- openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MICRO_SIZE);
+ size = ThumbnailConstants.MICRO_SIZE;
} else if (kind == ThumbnailConstants.MINI_KIND) {
- openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MINI_SIZE);
+ size = ThumbnailConstants.MINI_SIZE;
} else {
throw new IllegalArgumentException("Unsupported kind: " + kind);
}
@@ -674,9 +685,8 @@
}
}
- try (AssetFileDescriptor afd = cr.openTypedAssetFileDescriptor(uri,
- "image/*", openOpts, signal)) {
- return BitmapFactory.decodeFileDescriptor(afd.getFileDescriptor(), null, opts);
+ try {
+ return cr.loadThumbnail(uri, Point.convert(size), signal);
} catch (IOException e) {
Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
return null;
@@ -693,6 +703,7 @@
* Only the original process which made the request can cancel their own
* requests.
*/
+ @Deprecated
static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
synchronized (sPending) {
final CancellationSignal signal = sPending.get(uri);
@@ -936,9 +947,8 @@
}
/**
- * This class allows developers to query and get two kinds of thumbnails:
- * MINI_KIND: 512 x 384 thumbnail
- * MICRO_KIND: 96 x 96 thumbnail
+ * This class provides utility methods to obtain thumbnails for various
+ * {@link Images} items.
*/
public static class Thumbnails implements BaseColumns {
public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
@@ -958,13 +968,19 @@
}
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver
- * @param origId original image id
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
+ @Deprecated
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
final Uri uri = ContentUris.withAppendedId(
Images.Media.EXTERNAL_CONTENT_URI, origId);
@@ -972,51 +988,66 @@
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Return thumbnail representing a specific image item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image
- * associated with origId doesn't exist or memory is not enough.
+ * @param imageId the image item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long imageId, int kind,
BitmapFactory.Options options) {
final Uri uri = ContentUris.withAppendedId(
- Images.Media.EXTERNAL_CONTENT_URI, origId);
+ Images.Media.EXTERNAL_CONTENT_URI, imageId);
return InternalThumbnails.getThumbnail(cr, uri, kind, options);
}
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver
- * @param origId original image id
- * @param groupId the same groupId used in getThumbnail.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
+ @Deprecated
+ public static void cancelThumbnailRequest(ContentResolver cr, long origId,
+ long groupId) {
cancelThumbnailRequest(cr, origId);
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Return thumbnail representing a specific image item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param groupId the id of group to which this request belongs
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image
- * associated with origId doesn't exist or memory is not enough.
+ * @param imageId the image item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long imageId, long groupId,
int kind, BitmapFactory.Options options) {
- return getThumbnail(cr, origId, kind, options);
+ return getThumbnail(cr, imageId, kind, options);
}
/**
@@ -1059,7 +1090,16 @@
* access.
* <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#loadThumbnail}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
@@ -1509,7 +1549,16 @@
* access.
* <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
@@ -1790,7 +1839,16 @@
/**
* Cached album art.
* <P>Type: TEXT</P>
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#loadThumbnail}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String ALBUM_ART = "album_art";
}
@@ -2009,20 +2067,24 @@
}
/**
- * This class allows developers to query and get two kinds of thumbnails:
- * MINI_KIND: 512 x 384 thumbnail
- * MICRO_KIND: 96 x 96 thumbnail
- *
+ * This class provides utility methods to obtain thumbnails for various
+ * {@link Video} items.
*/
public static class Thumbnails implements BaseColumns {
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver
- * @param origId original video id
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
+ @Deprecated
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
final Uri uri = ContentUris.withAppendedId(
Video.Media.EXTERNAL_CONTENT_URI, origId);
@@ -2030,51 +2092,66 @@
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Return thumbnail representing a specific video item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image
- * associated with origId doesn't exist or memory is not enough.
+ * @param videoId the video item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind,
BitmapFactory.Options options) {
final Uri uri = ContentUris.withAppendedId(
- Video.Media.EXTERNAL_CONTENT_URI, origId);
+ Video.Media.EXTERNAL_CONTENT_URI, videoId);
return InternalThumbnails.getThumbnail(cr, uri, kind, options);
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param groupId the id of group to which this request belongs
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image associated with
- * origId doesn't exist or memory is not enough.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
- int kind, BitmapFactory.Options options) {
- return getThumbnail(cr, origId, kind, options);
+ @Deprecated
+ public static void cancelThumbnailRequest(ContentResolver cr, long videoId,
+ long groupId) {
+ cancelThumbnailRequest(cr, videoId);
}
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Return thumbnail representing a specific video item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver
- * @param origId original video id
- * @param groupId the same groupId used in getThumbnail.
+ * @param videoId the video item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
- cancelThumbnailRequest(cr, origId);
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long videoId, long groupId,
+ int kind, BitmapFactory.Options options) {
+ return getThumbnail(cr, videoId, kind, options);
}
/**
@@ -2110,14 +2187,17 @@
/**
* Path to the thumbnail file on disk.
* <p>
- * Note that apps may not have filesystem permissions to directly
- * access this path. Instead of trying to open this path directly,
- * apps should use
- * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
- * access.
- * <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index acb7577..ad64021 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1473,12 +1473,13 @@
* <p> If an user action is disabled by policy, this dialog can be triggered to let
* the user know about this.
* <p>
- * Input: Nothing.
+ * Input: {@link Intent#EXTRA_USER}: The user of the admin.
* <p>
* Output: Nothing.
*
* @hide
*/
+ // Intent#EXTRA_USER_ID can also be used
@SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS
@@ -1613,6 +1614,11 @@
public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
/**
+ * @hide - Private call() method on SettingsProvider to read from 'config' table.
+ */
+ public static final String CALL_METHOD_GET_CONFIG = "GET_config";
+
+ /**
* @hide - Specifies that the caller of the fast-path call()-based flow tracks
* the settings generation in order to cache values locally. If this key is
* mapped to a <code>null</code> string extra in the request bundle, the response
@@ -1671,9 +1677,15 @@
/** @hide - Private call() method to write to 'global' table */
public static final String CALL_METHOD_PUT_GLOBAL= "PUT_global";
+ /** @hide - Private call() method to write to 'configuration' table */
+ public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
+
/** @hide - Private call() method to reset to defaults the 'global' table */
public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
+ /** @hide - Private call() method to reset to defaults the 'configuration' table */
+ public static final String CALL_METHOD_RESET_CONFIG = "RESET_config";
+
/** @hide - Private call() method to reset to defaults the 'secure' table */
public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
@@ -11631,6 +11643,12 @@
public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
/**
+ * Addition app for GPU layer discovery
+ * @hide
+ */
+ public static final String GPU_DEBUG_LAYER_APP = "gpu_debug_layer_app";
+
+ /**
* Control whether the process CPU usage meter should be shown.
*
* @deprecated This functionality is no longer available as of
@@ -12363,6 +12381,28 @@
"sms_access_restriction_enabled";
/**
+ * If set to 1, an app must have the READ_PRIVILEGED_PHONE_STATE permission (or be a device
+ * / profile owner with the READ_PHONE_STATE permission) to access device identifiers.
+ *
+ * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+ *
+ * @hide
+ */
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED =
+ "privileged_device_identifier_check_enabled";
+
+ /**
+ * If set to 1, an app that is targeting Q and does not meet the new requirements to access
+ * device identifiers will receive a SecurityException.
+ *
+ * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+ *
+ * @hide
+ */
+ public static final String PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED =
+ "privileged_device_identifier_target_q_behavior_enabled";
+
+ /**
* If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
* and restoring to lower version of platform API will be skipped.
*
@@ -13432,6 +13472,112 @@
}
/**
+ * Configuration system settings, containing settings which are applied identically for all
+ * defined users. Only Android can read these and only a specific configuration service can
+ * write these.
+ *
+ * @hide
+ */
+ public static final class Config extends NameValueTable {
+ /**
+ * The content:// style URL for the config table.
+ *
+ * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a
+ * System API.
+ */
+ private static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/config");
+
+ private static final ContentProviderHolder sProviderHolder =
+ new ContentProviderHolder(CONTENT_URI);
+
+ // Populated lazily, guarded by class object:
+ private static final NameValueCache sNameValueCache = new NameValueCache(
+ CONTENT_URI,
+ CALL_METHOD_GET_CONFIG,
+ CALL_METHOD_PUT_CONFIG,
+ sProviderHolder);
+
+ /**
+ * Look up a name in the database.
+ * @param resolver to access the database with
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ *
+ * @hide
+ */
+ // TODO(b/117663715): require a new read permission
+ static String getString(ContentResolver resolver, String name) {
+ return sNameValueCache.getStringForUser(resolver, name, resolver.getUserId());
+ }
+
+ /**
+ * Store a name/value pair into the database.
+ * <p>
+ * The method takes an optional tag to associate with the setting which can be used to clear
+ * only settings made by your package and associated with this tag by passing the tag to
+ * {@link #resetToDefaults(ContentResolver, String)}. The value of this setting can be
+ * overridden by future calls to this or other put methods, and the tag provided in those
+ * calls, which may be null, will override the tag provided in this call. Any call to a put
+ * method which does not accept a tag will effectively set the tag to null.
+ * </p><p>
+ * Also the method takes an argument whether to make the value the default for this setting.
+ * If the system already specified a default value, then the one passed in here will
+ * <strong>not</strong> be set as the default.
+ * </p>
+ *
+ * @param resolver to access the database with.
+ * @param name to store.
+ * @param value to associate with the name.
+ * @param tag to associated with the setting.
+ * @param makeDefault whether to make the value the default one.
+ * @return true if the value was set, false on database errors.
+ *
+ * @see #resetToDefaults(ContentResolver, String)
+ *
+ * @hide
+ */
+ // TODO(b/117663715): require a new write permission restricted to a single source
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ static boolean putString(@NonNull ContentResolver resolver,
+ @NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault) {
+ return sNameValueCache.putStringForUser(resolver, name, value, tag, makeDefault,
+ resolver.getUserId());
+ }
+
+ /**
+ * Reset the settings to their defaults. This would reset <strong>only</strong> settings set
+ * by the caller's package. Passing in the optional tag will reset only settings changed by
+ * your package and associated with this tag.
+ *
+ * @param resolver Handle to the content resolver.
+ * @param tag Optional tag which should be associated with the settings to reset.
+ *
+ * @see #putString(ContentResolver, String, String, String, boolean)
+ *
+ * @hide
+ */
+ // TODO(b/117663715): require a new write permission restricted to a single source
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ static void resetToDefaults(@NonNull ContentResolver resolver,
+ @Nullable String tag) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
+ if (tag != null) {
+ arg.putString(CALL_METHOD_TAG_KEY, tag);
+ }
+ arg.putInt(CALL_METHOD_RESET_MODE_KEY, RESET_MODE_PACKAGE_DEFAULTS);
+ IContentProvider cp = sProviderHolder.getProvider(resolver);
+ cp.call(resolver.getPackageName(), CALL_METHOD_RESET_CONFIG, null, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI, e);
+ }
+ }
+ }
+
+ /**
* User-defined bookmarks and shortcuts. The target of each bookmark is an
* Intent URL, allowing it to be either a web page or a particular
* application activity.
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index c748c87..035b226 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -113,19 +113,6 @@
}
/**
- * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
- *
- * See implementation for binary key format.
- *
- * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
- * @removed
- */
- @Deprecated
- public @NonNull byte[] getTrustedHardwarePublicKey() {
- throw new UnsupportedOperationException();
- }
-
- /**
* CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
*/
public @NonNull CertPath getTrustedHardwareCertPath() {
@@ -223,18 +210,6 @@
}
/**
- * Sets public key used to encrypt recovery blob.
- *
- * @param publicKey The public key
- * @return This builder.
- * @removed Use {@link #setTrustedHardwareCertPath} instead.
- */
- @Deprecated
- public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
- throw new UnsupportedOperationException();
- }
-
- /**
* Sets CertPath used to validate the trusted hardware public key. The CertPath should
* contain a certificate of the trusted hardware public key and any necessary intermediate
* certificates.
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 70054fc..31a5962 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -23,7 +23,6 @@
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -301,18 +300,6 @@
}
/**
- * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void initRecoveryService(
- @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
- throws CertificateException, InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Initializes the recovery service for the calling application. The detailed steps should be:
* <ol>
* <li>Parse {@code signatureFile} to get relevant information.
@@ -363,16 +350,6 @@
}
/**
- * @deprecated Use {@link #getKeyChainSnapshot()}
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public @Nullable KeyChainSnapshot getRecoveryData() throws InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Returns data necessary to store all recoverable keys. Key material is
* encrypted with user secret and recovery public key.
*
@@ -440,17 +417,6 @@
}
/**
- * @deprecated Use {@link #getAliases()}.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public List<String> getAliases(@Nullable String packageName)
- throws InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Returns a list of aliases of keys belonging to the application.
*/
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -466,18 +432,6 @@
}
/**
- * @deprecated Use {@link #setRecoveryStatus(String, int)}
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public void setRecoveryStatus(
- @NonNull String packageName, String alias, int status)
- throws NameNotFoundException, InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Sets the recovery status for given key. It is used to notify the keystore that the key was
* successfully stored on the server or that there was an error. An application can check this
* value using {@link #getRecoveryStatus(String, String)}.
@@ -501,17 +455,6 @@
}
/**
- * @deprecated Use {@link #getRecoveryStatus(String)}.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public int getRecoveryStatus(String packageName, String alias)
- throws InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Returns the recovery status for the key with the given {@code alias}.
*
* <ul>
@@ -584,39 +527,6 @@
}
/**
- * Deprecated.
- * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
- * key store. Returns the raw material of the key.
- *
- * @param alias The key alias.
- * @param account The account associated with the key
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
- * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
- * to generate recoverable keys, as the snapshots are encrypted using a key derived from the
- * lock screen.
- * @deprecated Use {@link #generateKey(String)}
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
- throws InternalRecoveryServiceException, LockScreenRequiredException {
- throw new UnsupportedOperationException("Operation is not supported, use generateKey");
- }
-
- /**
- * @deprecated Use {@link #generateKey(String)}.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public Key generateKey(@NonNull String alias, byte[] account)
- throws InternalRecoveryServiceException, LockScreenRequiredException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Generates a recoverable key with the given {@code alias}.
*
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 3bb6421..42e7182 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -78,36 +78,6 @@
}
/**
- * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- @NonNull public byte[] start(
- @NonNull byte[] verifierPublicKey,
- @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge,
- @NonNull List<KeyChainProtectionParams> secrets)
- throws CertificateException, InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- @NonNull public byte[] start(
- @NonNull CertPath verifierCertPath,
- @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge,
- @NonNull List<KeyChainProtectionParams> secrets)
- throws CertificateException, InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Starts a recovery session and returns a blob with proof of recovery secret possession.
* The method generates a symmetric key for a session, which trusted remote device can use to
* return recovery key.
@@ -162,20 +132,6 @@
}
/**
- * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
- * @removed
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public Map<String, byte[]> recoverKeys(
- @NonNull byte[] recoveryKeyBlob,
- @NonNull List<WrappedApplicationKey> applicationKeys)
- throws SessionExpiredException, DecryptionFailedException,
- InternalRecoveryServiceException {
- throw new UnsupportedOperationException();
- }
-
- /**
* Imports key chain snapshot recovered from a remote vault.
*
* @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index 187a671..ae4448f 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -75,15 +75,6 @@
}
/**
- * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
- * @removed
- */
- @Deprecated
- public Builder setAccount(@NonNull byte[] account) {
- throw new UnsupportedOperationException();
- }
-
- /**
* Sets key material encrypted by recovery key.
*
* @param encryptedKeyMaterial The key material
@@ -133,15 +124,6 @@
return mEncryptedKeyMaterial;
}
- /**
- * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
- * @removed
- */
- @Deprecated
- public @NonNull byte[] getAccount() {
- throw new UnsupportedOperationException();
- }
-
public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
new Parcelable.Creator<WrappedApplicationKey>() {
public WrappedApplicationKey createFromParcel(Parcel in) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index d7fe15d..b8e0387 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -737,7 +737,8 @@
* <p>This method will throw a security exception if you don't have access to notifications
* for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
- * device} in order to use this method.
+ * device} or be the {@link NotificationAssistantService notification assistant} in order to
+ * use this method.
*
* @param pkg The package to retrieve channels for.
*/
@@ -760,7 +761,8 @@
* <p>This method will throw a security exception if you don't have access to notifications
* for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
- * device} in order to use this method.
+ * device} or be the {@link NotificationAssistantService notification assistant} in order to
+ * use this method.
*
* @param pkg The package to retrieve channel groups for.
*/
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 10d7911..ec63cd9 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1112,7 +1112,6 @@
@Override
protected void playImpl() {
- dispatchOnStart();
super.playImpl();
try {
mFileOutputStream.close();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c89617f..3ab8a0a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -23,6 +23,7 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.text.LineBreaker;
import android.text.method.TextKeyListener;
import android.text.style.AlignmentSpan;
import android.text.style.LeadingMarginSpan;
@@ -50,9 +51,9 @@
public abstract class Layout {
/** @hide */
@IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
- NativeLineBreaker.BREAK_STRATEGY_SIMPLE,
- NativeLineBreaker.BREAK_STRATEGY_HIGH_QUALITY,
- NativeLineBreaker.BREAK_STRATEGY_BALANCED
+ LineBreaker.BREAK_STRATEGY_SIMPLE,
+ LineBreaker.BREAK_STRATEGY_HIGH_QUALITY,
+ LineBreaker.BREAK_STRATEGY_BALANCED
})
@Retention(RetentionPolicy.SOURCE)
public @interface BreakStrategy {}
@@ -63,20 +64,19 @@
* before it (which yields a more consistent user experience when editing), but layout may not
* be the highest quality.
*/
- public static final int BREAK_STRATEGY_SIMPLE = NativeLineBreaker.BREAK_STRATEGY_SIMPLE;
+ public static final int BREAK_STRATEGY_SIMPLE = LineBreaker.BREAK_STRATEGY_SIMPLE;
/**
* Value for break strategy indicating high quality line breaking, including automatic
* hyphenation and doing whole-paragraph optimization of line breaks.
*/
- public static final int BREAK_STRATEGY_HIGH_QUALITY =
- NativeLineBreaker.BREAK_STRATEGY_HIGH_QUALITY;
+ public static final int BREAK_STRATEGY_HIGH_QUALITY = LineBreaker.BREAK_STRATEGY_HIGH_QUALITY;
/**
* Value for break strategy indicating balanced line breaking. The breaks are chosen to
* make all lines as close to the same length as possible, including automatic hyphenation.
*/
- public static final int BREAK_STRATEGY_BALANCED = NativeLineBreaker.BREAK_STRATEGY_BALANCED;
+ public static final int BREAK_STRATEGY_BALANCED = LineBreaker.BREAK_STRATEGY_BALANCED;
/** @hide */
@IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
@@ -94,32 +94,29 @@
* layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used
* as suggestions for potential line breaks.
*/
- public static final int HYPHENATION_FREQUENCY_NONE =
- NativeLineBreaker.HYPHENATION_FREQUENCY_NONE;
+ public static final int HYPHENATION_FREQUENCY_NONE = LineBreaker.HYPHENATION_FREQUENCY_NONE;
/**
* Value for hyphenation frequency indicating a light amount of automatic hyphenation, which
* is a conservative default. Useful for informal cases, such as short sentences or chat
* messages.
*/
- public static final int HYPHENATION_FREQUENCY_NORMAL =
- NativeLineBreaker.HYPHENATION_FREQUENCY_NORMAL;
+ public static final int HYPHENATION_FREQUENCY_NORMAL = LineBreaker.HYPHENATION_FREQUENCY_NORMAL;
/**
* Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical
* in typography. Useful for running text and where it's important to put the maximum amount of
* text in a screen with limited space.
*/
- public static final int HYPHENATION_FREQUENCY_FULL =
- NativeLineBreaker.HYPHENATION_FREQUENCY_FULL;
+ public static final int HYPHENATION_FREQUENCY_FULL = LineBreaker.HYPHENATION_FREQUENCY_FULL;
private static final ParagraphStyle[] NO_PARA_SPANS =
ArrayUtils.emptyArray(ParagraphStyle.class);
/** @hide */
@IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
- NativeLineBreaker.JUSTIFICATION_MODE_NONE,
- NativeLineBreaker.JUSTIFICATION_MODE_INTER_WORD
+ LineBreaker.JUSTIFICATION_MODE_NONE,
+ LineBreaker.JUSTIFICATION_MODE_INTER_WORD
})
@Retention(RetentionPolicy.SOURCE)
public @interface JustificationMode {}
@@ -127,13 +124,13 @@
/**
* Value for justification mode indicating no justification.
*/
- public static final int JUSTIFICATION_MODE_NONE = NativeLineBreaker.JUSTIFICATION_MODE_NONE;
+ public static final int JUSTIFICATION_MODE_NONE = LineBreaker.JUSTIFICATION_MODE_NONE;
/**
* Value for justification mode indicating the text is justified by stretching word spacing.
*/
public static final int JUSTIFICATION_MODE_INTER_WORD =
- NativeLineBreaker.JUSTIFICATION_MODE_INTER_WORD;
+ LineBreaker.JUSTIFICATION_MODE_INTER_WORD;
/*
* Line spacing multiplier for default line spacing.
@@ -1809,6 +1806,7 @@
}
}
+
/**
* Fills in the specified Path with a representation of a cursor
* at the specified offset. This will often be a vertical line
@@ -1824,7 +1822,6 @@
boolean clamped = shouldClampCursor(line);
float h1 = getPrimaryHorizontal(point, clamped) - 0.5f;
- float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1;
int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
@@ -1842,34 +1839,24 @@
if (h1 < 0.5f)
h1 = 0.5f;
- if (h2 < 0.5f)
- h2 = 0.5f;
- if (Float.compare(h1, h2) == 0) {
- dest.moveTo(h1, top);
- dest.lineTo(h1, bottom);
- } else {
- dest.moveTo(h1, top);
- dest.lineTo(h1, (top + bottom) >> 1);
-
- dest.moveTo(h2, (top + bottom) >> 1);
- dest.lineTo(h2, bottom);
- }
+ dest.moveTo(h1, top);
+ dest.lineTo(h1, bottom);
if (caps == 2) {
- dest.moveTo(h2, bottom);
- dest.lineTo(h2 - dist, bottom + dist);
- dest.lineTo(h2, bottom);
- dest.lineTo(h2 + dist, bottom + dist);
+ dest.moveTo(h1, bottom);
+ dest.lineTo(h1 - dist, bottom + dist);
+ dest.lineTo(h1, bottom);
+ dest.lineTo(h1 + dist, bottom + dist);
} else if (caps == 1) {
- dest.moveTo(h2, bottom);
- dest.lineTo(h2 - dist, bottom + dist);
+ dest.moveTo(h1, bottom);
+ dest.lineTo(h1 - dist, bottom + dist);
- dest.moveTo(h2 - dist, bottom + dist - 0.5f);
- dest.lineTo(h2 + dist, bottom + dist - 0.5f);
+ dest.moveTo(h1 - dist, bottom + dist - 0.5f);
+ dest.lineTo(h1 + dist, bottom + dist - 0.5f);
- dest.moveTo(h2 + dist, bottom + dist);
- dest.lineTo(h2, bottom);
+ dest.moveTo(h1 + dist, bottom + dist);
+ dest.lineTo(h1, bottom);
}
if (fn == 2) {
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 9bf8cd2..f9370a8 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.text.MeasuredText;
import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
@@ -121,7 +122,7 @@
private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
// The native MeasuredParagraph.
- private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;
+ private @Nullable MeasuredText mMeasuredText;
// Following two objects are for avoiding object allocation.
private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -149,7 +150,7 @@
mWidths.clear();
mFontMetrics.clear();
mSpanEndCache.clear();
- mNativeMeasuredParagraph = null;
+ mMeasuredText = null;
}
/**
@@ -245,8 +246,8 @@
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
* Returns null in other cases.
*/
- public NativeMeasuredParagraph getNativeMeasuredParagraph() {
- return mNativeMeasuredParagraph;
+ public MeasuredText getMeasuredText() {
+ return mMeasuredText;
}
/**
@@ -259,7 +260,7 @@
* @param end the exclusive end offset of the target region in the text
*/
public float getWidth(int start, int end) {
- if (mNativeMeasuredParagraph == null) {
+ if (mMeasuredText == null) {
// We have result in Java.
final float[] widths = mWidths.getRawArray();
float r = 0.0f;
@@ -269,7 +270,7 @@
return r;
} else {
// We have result in native.
- return mNativeMeasuredParagraph.getWidth(start, end);
+ return mMeasuredText.getWidth(start, end);
}
}
@@ -281,7 +282,7 @@
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect bounds) {
- mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
+ mMeasuredText.getBounds(start, end, bounds);
}
/**
@@ -290,7 +291,7 @@
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
*/
public float getCharWidthAt(@IntRange(from = 0) int offset) {
- return mNativeMeasuredParagraph.getCharWidthAt(offset);
+ return mMeasuredText.getCharWidthAt(offset);
}
/**
@@ -391,12 +392,13 @@
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
- final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
+ final MeasuredText.Builder builder = new MeasuredText.Builder(mt.mCopiedBuffer);
+ builder.setComputeHyphenation(computeHyphenation);
+ builder.setComputeLayout(computeLayout);
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
- mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
- computeLayout);
+ mt.mMeasuredText = builder.build();
} else {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
@@ -417,8 +419,7 @@
mt.mSpanEndCache.append(spanEnd);
}
}
- mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
- computeLayout);
+ mt.mMeasuredText = builder.build();
}
return mt;
@@ -490,7 +491,7 @@
private void applyReplacementRun(@NonNull ReplacementSpan replacement,
@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- @Nullable NativeMeasuredParagraph.Builder builder) {
+ @Nullable MeasuredText.Builder builder) {
// Use original text. Shouldn't matter.
// TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
// backward compatibility? or Should we initialize them for getFontMetricsInt?
@@ -504,13 +505,13 @@
}
mWholeWidth += width;
} else {
- builder.addReplacementRun(mCachedPaint, start, end, width);
+ builder.appendReplacementRun(mCachedPaint, end - start, width);
}
}
private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- @Nullable NativeMeasuredParagraph.Builder builder) {
+ @Nullable MeasuredText.Builder builder) {
if (mLtrWithoutBidi) {
// If the whole text is LTR direction, just apply whole region.
@@ -519,7 +520,7 @@
mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
mWidths.getRawArray(), start);
} else {
- builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
+ builder.appendStyleRun(mCachedPaint, end - start, false /* isRtl */);
}
} else {
// If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -535,7 +536,7 @@
mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
isRtl, mWidths.getRawArray(), levelStart);
} else {
- builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
+ builder.appendStyleRun(mCachedPaint, levelEnd - levelStart, isRtl);
}
if (levelEnd == end) {
break;
@@ -552,7 +553,7 @@
@Nullable MetricAffectingSpan[] spans,
@IntRange(from = 0) int start, // inclusive, in original text buffer
@IntRange(from = 0) int end, // exclusive, in original text buffer
- @Nullable NativeMeasuredParagraph.Builder builder) {
+ @Nullable MeasuredText.Builder builder) {
mCachedPaint.set(paint);
// XXX paint should not have a baseline shift, but...
mCachedPaint.baselineShift = 0;
@@ -658,6 +659,6 @@
* This only works if the MeasuredParagraph is computed with buildForStaticLayout.
*/
public @IntRange(from = 0) int getMemoryUsage() {
- return mNativeMeasuredParagraph.getMemoryUsage();
+ return mMeasuredText.getMemoryUsage();
}
}
diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java
deleted file mode 100644
index bfdccca..0000000
--- a/core/java/android/text/NativeMeasuredParagraph.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2010 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.text;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-import dalvik.annotation.optimization.CriticalNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-/**
- * A native implementation of measured paragraph.
- * TODO: Consider to make this class public.
- * @hide
- */
-public class NativeMeasuredParagraph {
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
- private long mNativePtr;
- private @NonNull char[] mChars;
-
- // Use builder instead.
- private NativeMeasuredParagraph(long ptr, @NonNull char[] chars) {
- mNativePtr = ptr;
- mChars = chars;
- }
-
- /**
- * Returns a characters of this paragraph.
- */
- public char[] getChars() {
- return mChars;
- }
-
- /**
- * Returns a width of the given region
- */
- public float getWidth(int start, int end) {
- return nGetWidth(mNativePtr, start, end);
- }
-
- /**
- * Returns a memory usage of the native object.
- */
- public int getMemoryUsage() {
- return nGetMemoryUsage(mNativePtr);
- }
-
- /**
- * Fills the boundary box of the given region
- */
- public void getBounds(char[] buf, int start, int end, Rect rect) {
- nGetBounds(mNativePtr, buf, start, end, rect);
- }
-
- /**
- * Returns the width of the character at the given offset
- */
- public float getCharWidthAt(int offset) {
- return nGetCharWidthAt(mNativePtr, offset);
- }
-
- /**
- * Returns a native pointer of the underlying native object.
- */
- public long getNativePtr() {
- return mNativePtr;
- }
-
- @CriticalNative
- private static native float nGetWidth(/* Non Zero */ long nativePtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end);
-
- @CriticalNative
- private static native /* Non Zero */ long nGetReleaseFunc();
-
- @CriticalNative
- private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
-
- private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
- Rect rect);
-
- @CriticalNative
- private static native float nGetCharWidthAt(long nativePtr, int offset);
-
- /**
- * A builder for the NativeMeasuredParagraph
- */
- public static class Builder {
- private final long mNativePtr;
-
- public Builder() {
- mNativePtr = nInitBuilder();
- }
-
- /**
- * Apply styles to given range
- */
- public void addStyleRun(@NonNull Paint paint, int start, int end, boolean isRtl) {
- nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
- }
-
- /**
- * Tells native that the given range is replaced with the object of given width.
- */
- public void addReplacementRun(@NonNull Paint paint, int start, int end, float width) {
- nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
- }
-
- /**
- * Build the NativeMeasuredParagraph
- */
- public NativeMeasuredParagraph build(char[] text, boolean computeHyphenation,
- boolean computeLayout) {
- try {
- long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation,
- computeLayout);
- NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr, text);
- sRegistry.registerNativeAllocation(res, ptr);
- return res;
- } finally {
- nFreeBuilder(mNativePtr);
- }
- }
-
- private static native /* Non Zero */ long nInitBuilder();
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- boolean isRtl);
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @FloatRange(from = 0) float width);
-
- private static native long nBuildNativeMeasuredParagraph(
- /* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text,
- boolean computeHyphenation,
- boolean computeLayout);
-
- private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
- }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d2f0853..2cf0262 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
+import android.graphics.text.LineBreaker;
import android.os.Build;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
@@ -55,7 +56,7 @@
*
* - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
* native.
- * - Run NativeLineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
+ * - Run LineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
*/
@@ -634,7 +635,7 @@
indents = null;
}
- final NativeLineBreaker lineBreaker = new NativeLineBreaker.Builder()
+ final LineBreaker lineBreaker = new LineBreaker.Builder()
.setBreakStrategy(b.mBreakStrategy)
.setHyphenationFrequency(b.mHyphenationFrequency)
// TODO: Support more justification mode, e.g. letter spacing, stretching.
@@ -642,8 +643,8 @@
.setIndents(indents)
.build();
- NativeLineBreaker.ParagraphConstraints constraints =
- new NativeLineBreaker.ParagraphConstraints();
+ LineBreaker.ParagraphConstraints constraints =
+ new LineBreaker.ParagraphConstraints();
PrecomputedText.ParagraphInfo[] paragraphInfo = null;
final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
@@ -739,8 +740,8 @@
constraints.setIndent(firstWidth, firstWidthLineCount);
constraints.setTabStops(variableTabStops, TAB_INCREMENT);
- NativeLineBreaker.Result res = lineBreaker.computeLineBreaks(
- measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount);
+ LineBreaker.Result res = lineBreaker.computeLineBreaks(
+ measuredPara.getMeasuredText(), constraints, mLineCount);
int breakCount = res.getLineCount();
if (lineBreakCapacity < breakCount) {
lineBreakCapacity = breakCount;
@@ -776,7 +777,7 @@
width += lineWidths[i];
} else {
for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
- width += measuredPara.getCharWidthAt(j - paraStart);
+ width += measuredPara.getCharWidthAt(j);
}
}
hasTab |= hasTabs[i];
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index e31e928..195de07 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -16,7 +16,10 @@
package android.text;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,6 +45,7 @@
import android.text.style.EasyEditSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
+import android.text.style.LineBackgroundSpan;
import android.text.style.LocaleSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
@@ -69,7 +73,9 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
import java.lang.reflect.Array;
+import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@@ -87,6 +93,44 @@
private static final String ELLIPSIS_NORMAL = "\u2026"; // HORIZONTAL ELLIPSIS (…)
private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // TWO DOT LEADER (‥)
+ private static final int LINE_FEED_CODE_POINT = 10;
+ private static final int NBSP_CODE_POINT = 160;
+
+ /**
+ * Flags for {@link #makeSafeForPresentation(String, int, float, int)}
+ *
+ * @hide
+ */
+ @Retention(SOURCE)
+ @IntDef(flag = true, prefix = "CLEAN_STRING_FLAG_",
+ value = {SAFE_STRING_FLAG_TRIM, SAFE_STRING_FLAG_SINGLE_LINE,
+ SAFE_STRING_FLAG_FIRST_LINE})
+ public @interface SafeStringFlags {}
+
+ /**
+ * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
+ * of the label.
+ *
+ * @see #makeSafeForPresentation(String, int, float, int)
+ */
+ public static final int SAFE_STRING_FLAG_TRIM = 0x1;
+
+ /**
+ * Force entire string into single line of text (no newlines). Cannot be set at the same time as
+ * {@link #SAFE_STRING_FLAG_FIRST_LINE}.
+ *
+ * @see #makeSafeForPresentation(String, int, float, int)
+ */
+ public static final int SAFE_STRING_FLAG_SINGLE_LINE = 0x2;
+
+ /**
+ * Return only first line of text (truncate at first newline). Cannot be set at the same time as
+ * {@link #SAFE_STRING_FLAG_SINGLE_LINE}.
+ *
+ * @see #makeSafeForPresentation(String, int, float, int)
+ */
+ public static final int SAFE_STRING_FLAG_FIRST_LINE = 0x4;
+
/** {@hide} */
@NonNull
public static String getEllipsisString(@NonNull TextUtils.TruncateAt method) {
@@ -687,7 +731,9 @@
/** @hide */
public static final int ACCESSIBILITY_URL_SPAN = 26;
/** @hide */
- public static final int LAST_SPAN = ACCESSIBILITY_URL_SPAN;
+ public static final int LINE_BACKGROUND_SPAN = 27;
+ /** @hide */
+ public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -878,6 +924,10 @@
readSpan(p, sp, new AccessibilityURLSpan(p));
break;
+ case LINE_BACKGROUND_SPAN:
+ readSpan(p, sp, new LineBackgroundSpan.Standard(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
@@ -2116,6 +2166,222 @@
return trimmed;
}
+ private static boolean isNewline(int codePoint) {
+ int type = Character.getType(codePoint);
+ return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
+ || codePoint == LINE_FEED_CODE_POINT;
+ }
+
+ private static boolean isWhiteSpace(int codePoint) {
+ return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
+ }
+
+ /**
+ * Remove html, remove bad characters, and truncate string.
+ *
+ * <p>This method is meant to remove common mistakes and nefarious formatting from strings that
+ * were loaded from untrusted sources (such as other packages).
+ *
+ * <p>This method first {@link Html#fromHtml treats the string like HTML} and then ...
+ * <ul>
+ * <li>Removes new lines or truncates at first new line
+ * <li>Trims the white-space off the end
+ * <li>Truncates the string
+ * </ul>
+ * ... if specified.
+ *
+ * @param unclean The input string
+ * @param maxCharactersToConsider The maximum number of characters of {@code unclean} to
+ * consider from the input string. {@code 0} disables this
+ * feature.
+ * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42.
+ * This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br />
+ * Usually ellipsizing should be left to the view showing the string. If a
+ * string is used as an input to another string, it might be useful to
+ * control the length of the input string though. {@code 0} disables this
+ * feature.
+ * @param flags Flags controlling cleaning behavior (Can be {@link #SAFE_STRING_FLAG_TRIM},
+ * {@link #SAFE_STRING_FLAG_SINGLE_LINE},
+ * and {@link #SAFE_STRING_FLAG_FIRST_LINE})
+ *
+ * @return The cleaned string
+ */
+ public static @NonNull CharSequence makeSafeForPresentation(@NonNull String unclean,
+ @IntRange(from = 0) int maxCharactersToConsider,
+ @FloatRange(from = 0) float ellipsizeDip, @SafeStringFlags int flags) {
+ boolean onlyKeepFirstLine = ((flags & SAFE_STRING_FLAG_FIRST_LINE) != 0);
+ boolean forceSingleLine = ((flags & SAFE_STRING_FLAG_SINGLE_LINE) != 0);
+ boolean trim = ((flags & SAFE_STRING_FLAG_TRIM) != 0);
+
+ Preconditions.checkNotNull(unclean);
+ Preconditions.checkArgumentNonnegative(maxCharactersToConsider);
+ Preconditions.checkArgumentNonNegative(ellipsizeDip, "ellipsizeDip");
+ Preconditions.checkFlagsArgument(flags, SAFE_STRING_FLAG_TRIM
+ | SAFE_STRING_FLAG_SINGLE_LINE | SAFE_STRING_FLAG_FIRST_LINE);
+ Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine),
+ "Cannot set SAFE_STRING_FLAG_SINGLE_LINE and SAFE_STRING_FLAG_FIRST_LINE at the"
+ + "same time");
+
+ String shortString;
+ if (maxCharactersToConsider > 0) {
+ shortString = unclean.substring(0, Math.min(unclean.length(), maxCharactersToConsider));
+ } else {
+ shortString = unclean;
+ }
+
+ // Treat string as HTML. This
+ // - converts HTML symbols: e.g. ß -> ß
+ // - applies some HTML tags: e.g. <br> -> \n
+ // - removes invalid characters such as \b
+ // - removes html styling, such as <b>
+ // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc
+ // - replaces some html tags by "object replacement" markers: <img> -> \ufffc
+ // - Removes leading white space
+ // - Removes all trailing white space beside a single space
+ // - Collapses double white space
+ StringWithRemovedChars gettingCleaned = new StringWithRemovedChars(
+ Html.fromHtml(shortString).toString());
+
+ int firstNonWhiteSpace = -1;
+ int firstTrailingWhiteSpace = -1;
+
+ // Remove new lines (if requested) and control characters.
+ int uncleanLength = gettingCleaned.length();
+ for (int offset = 0; offset < uncleanLength; ) {
+ int codePoint = gettingCleaned.codePointAt(offset);
+ int type = Character.getType(codePoint);
+ int codePointLen = Character.charCount(codePoint);
+ boolean isNewline = isNewline(codePoint);
+
+ if (onlyKeepFirstLine && isNewline) {
+ gettingCleaned.removeAllCharAfter(offset);
+ break;
+ } else if (forceSingleLine && isNewline) {
+ gettingCleaned.removeRange(offset, offset + codePointLen);
+ } else if (type == Character.CONTROL && !isNewline) {
+ gettingCleaned.removeRange(offset, offset + codePointLen);
+ } else if (trim && !isWhiteSpace(codePoint)) {
+ // This is only executed if the code point is not removed
+ if (firstNonWhiteSpace == -1) {
+ firstNonWhiteSpace = offset;
+ }
+ firstTrailingWhiteSpace = offset + codePointLen;
+ }
+
+ offset += codePointLen;
+ }
+
+ if (trim) {
+ // Remove leading and trailing white space
+ if (firstNonWhiteSpace == -1) {
+ // No non whitespace found, remove all
+ gettingCleaned.removeAllCharAfter(0);
+ } else {
+ if (firstNonWhiteSpace > 0) {
+ gettingCleaned.removeAllCharBefore(firstNonWhiteSpace);
+ }
+ if (firstTrailingWhiteSpace < uncleanLength) {
+ gettingCleaned.removeAllCharAfter(firstTrailingWhiteSpace);
+ }
+ }
+ }
+
+ if (ellipsizeDip == 0) {
+ return gettingCleaned.toString();
+ } else {
+ // Truncate
+ final TextPaint paint = new TextPaint();
+ paint.setTextSize(42);
+
+ return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
+ TextUtils.TruncateAt.END);
+ }
+ }
+
+ /**
+ * A special string manipulation class. Just records removals and executes the when onString()
+ * is called.
+ */
+ private static class StringWithRemovedChars {
+ /** The original string */
+ private final String mOriginal;
+
+ /**
+ * One bit per char in string. If bit is set, character needs to be removed. If whole
+ * bit field is not initialized nothing needs to be removed.
+ */
+ private BitSet mRemovedChars;
+
+ StringWithRemovedChars(@NonNull String original) {
+ mOriginal = original;
+ }
+
+ /**
+ * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including
+ * firstNonRemoved) as removed.
+ */
+ void removeRange(int firstRemoved, int firstNonRemoved) {
+ if (mRemovedChars == null) {
+ mRemovedChars = new BitSet(mOriginal.length());
+ }
+
+ mRemovedChars.set(firstRemoved, firstNonRemoved);
+ }
+
+ /**
+ * Remove all characters before {@code firstNonRemoved}.
+ */
+ void removeAllCharBefore(int firstNonRemoved) {
+ if (mRemovedChars == null) {
+ mRemovedChars = new BitSet(mOriginal.length());
+ }
+
+ mRemovedChars.set(0, firstNonRemoved);
+ }
+
+ /**
+ * Remove all characters after and including {@code firstRemoved}.
+ */
+ void removeAllCharAfter(int firstRemoved) {
+ if (mRemovedChars == null) {
+ mRemovedChars = new BitSet(mOriginal.length());
+ }
+
+ mRemovedChars.set(firstRemoved, mOriginal.length());
+ }
+
+ @Override
+ public String toString() {
+ // Common case, no chars removed
+ if (mRemovedChars == null) {
+ return mOriginal;
+ }
+
+ StringBuilder sb = new StringBuilder(mOriginal.length());
+ for (int i = 0; i < mOriginal.length(); i++) {
+ if (!mRemovedChars.get(i)) {
+ sb.append(mOriginal.charAt(i));
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Return length or the original string
+ */
+ int length() {
+ return mOriginal.length();
+ }
+
+ /**
+ * Return codePoint of original string at a certain {@code offset}
+ */
+ int codePointAt(int offset) {
+ return mOriginal.codePointAt(offset);
+ }
+ }
+
private static Object sLock = new Object();
private static char[] sTemp = null;
diff --git a/core/java/android/text/style/LineBackgroundSpan.java b/core/java/android/text/style/LineBackgroundSpan.java
index 9c7859f..5a55fd7 100644
--- a/core/java/android/text/style/LineBackgroundSpan.java
+++ b/core/java/android/text/style/LineBackgroundSpan.java
@@ -16,15 +16,110 @@
package android.text.style;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
-public interface LineBackgroundSpan
-extends ParagraphStyle
+/**
+ * Used to change the background of lines where the span is attached to.
+ */
+public interface LineBackgroundSpan extends ParagraphStyle
{
- public void drawBackground(Canvas c, Paint p,
- int left, int right,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- int lnum);
+ /**
+ * Draw the background on the canvas.
+ *
+ * @param canvas canvas on which the span should be rendered
+ * @param paint paint used to draw text, which should be left unchanged on exit
+ * @param left left position of the line relative to input canvas, in pixels
+ * @param right right position of the line relative to input canvas, in pixels
+ * @param top top position of the line relative to input canvas, in pixels
+ * @param baseline baseline of the text relative to input canvas, in pixels
+ * @param bottom bottom position of the line relative to input canvas, in pixels
+ * @param text current text
+ * @param start start character index of the line
+ * @param end end character index of the line
+ * @param lineNumber line number in the current text layout
+ */
+ void drawBackground(@NonNull Canvas canvas, @NonNull Paint paint,
+ @Px int left, @Px int right,
+ @Px int top, @Px int baseline, @Px int bottom,
+ @NonNull CharSequence text, int start, int end,
+ int lineNumber);
+ /**
+ * Default implementation of the {@link LineBackgroundSpan}, which changes the background
+ * color of the lines to which the span is attached.
+ */
+ class Standard implements LineBackgroundSpan, ParcelableSpan {
+
+ private final int mColor;
+
+ /**
+ * Constructor taking a color integer.
+ *
+ * @param color Color integer that defines the background color.
+ */
+ public Standard(@ColorInt int color) {
+ mColor = color;
+ }
+
+ /**
+ * Creates a {@link LineBackgroundSpan.Standard} from a parcel
+ */
+ public Standard(@NonNull Parcel src) {
+ mColor = src.readInt();
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.LINE_BACKGROUND_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mColor);
+ }
+
+ /**
+ * @return the color of this span.
+ * @see Standard#Standard(int)
+ */
+ @ColorInt
+ public final int getColor() {
+ return mColor;
+ }
+
+ @Override
+ public void drawBackground(@NonNull Canvas canvas, @NonNull Paint paint,
+ @Px int left, @Px int right,
+ @Px int top, @Px int baseline, @Px int bottom,
+ @NonNull CharSequence text, int start, int end,
+ int lineNumber) {
+ final int originColor = paint.getColor();
+ paint.setColor(mColor);
+ canvas.drawRect(left, right, top, bottom, paint);
+ paint.setColor(originColor);
+ }
+ }
}
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 648bd1b..2355769 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -22,7 +22,8 @@
import android.content.res.TypedArray;
import android.graphics.LeakyTypefaceStorage;
import android.graphics.Typeface;
-import android.graphics.fonts.Font;
+import android.graphics.fonts.FontStyle;
+import android.os.LocaleList;
import android.os.Parcel;
import android.text.ParcelableSpan;
import android.text.TextPaint;
@@ -66,6 +67,7 @@
private final Typeface mTypeface;
private final int mTextFontWeight;
+ private final LocaleList mTextLocales;
private final float mShadowRadius;
private final float mShadowDx;
@@ -149,6 +151,19 @@
mTextFontWeight = a.getInt(com.android.internal.R.styleable
.TextAppearance_textFontWeight, -1);
+ final String localeString = a.getString(com.android.internal.R.styleable
+ .TextAppearance_textLocale);
+ if (localeString != null) {
+ LocaleList localeList = LocaleList.forLanguageTags(localeString);
+ if (!localeList.isEmpty()) {
+ mTextLocales = localeList;
+ } else {
+ mTextLocales = null;
+ }
+ } else {
+ mTextLocales = null;
+ }
+
mShadowRadius = a.getFloat(com.android.internal.R.styleable
.TextAppearance_shadowRadius, 0.0f);
mShadowDx = a.getFloat(com.android.internal.R.styleable
@@ -201,6 +216,7 @@
mTypeface = null;
mTextFontWeight = -1;
+ mTextLocales = null;
mShadowRadius = 0.0f;
mShadowDx = 0.0f;
@@ -233,6 +249,7 @@
mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
mTextFontWeight = src.readInt();
+ mTextLocales = src.readParcelable(LocaleList.class.getClassLoader());
mShadowRadius = src.readFloat();
mShadowDx = src.readFloat();
@@ -285,6 +302,7 @@
LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
dest.writeInt(mTextFontWeight);
+ dest.writeParcelable(mTextLocales, flags);
dest.writeFloat(mShadowRadius);
dest.writeFloat(mShadowDx);
@@ -349,6 +367,15 @@
}
/**
+ * Returns the {@link android.os.LocaleList} specified by this span, or <code>null</code>
+ * if it does not specify one.
+ */
+ @Nullable
+ public LocaleList getTextLocales() {
+ return mTextLocales;
+ }
+
+ /**
* Returns the typeface specified by this span, or <code>null</code>
* if it does not specify one.
*/
@@ -463,7 +490,7 @@
if (styledTypeface != null) {
final Typeface readyTypeface;
if (mTextFontWeight >= 0) {
- final int weight = Math.min(Font.FONT_WEIGHT_MAX, mTextFontWeight);
+ final int weight = Math.min(FontStyle.FONT_WEIGHT_MAX, mTextFontWeight);
final boolean italic = (style & Typeface.ITALIC) != 0;
readyTypeface = ds.setTypeface(Typeface.create(styledTypeface, weight, italic));
} else {
@@ -487,6 +514,10 @@
ds.setTextSize(mTextSize);
}
+ if (mTextLocales != null) {
+ ds.setTextLocales(mTextLocales);
+ }
+
if (mHasElegantTextHeight) {
ds.setElegantTextHeight(mElegantTextHeight);
}
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 4608e20..0e5252e 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -315,6 +315,7 @@
ArrayList<Transition> currentTransitions =
runningTransitions.get(mSceneRoot);
currentTransitions.remove(transition);
+ transition.removeListener(this);
}
});
mTransition.captureValues(mSceneRoot, false);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 183e833..db2c190 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -47,7 +47,7 @@
DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
- DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
+ DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false");
}
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index 209451b..cd2b6ce 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -737,8 +737,7 @@
fillBuffer();
if (mOffset + n <= mEnd) {
// fast path read. String is well within the current buffer
- String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n,
- StandardCharsets.UTF_8);
+ String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8);
incOffset(n);
return value;
} else if (n <= mBufferSize) {
@@ -752,14 +751,13 @@
mDiscardedBytes += mOffset;
mOffset = 0;
- String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n,
- StandardCharsets.UTF_8);
+ String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8);
incOffset(n);
return value;
}
// Otherwise, the string is too large to use the buffer. Create the string from a
// separate byte array.
- return StringFactory.newStringFromBytes(readRawBytes(n), 0, n, StandardCharsets.UTF_8);
+ return new String(readRawBytes(n), 0, n, StandardCharsets.UTF_8);
}
/**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 3e559d9..0c3a295 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -226,27 +226,52 @@
int getPreferredOptionsPanelGravity(int displayId);
/**
- * Lock the device orientation to the specified rotation, or to the
- * current rotation if -1. Sensor input will be ignored until
- * thawRotation() is called.
- * @hide
+ * Equivalent to calling {@link #freezeDisplayRotation(int, int)} with {@link
+ * android.view.Display#DEFAULT_DISPLAY} and given rotation.
*/
void freezeRotation(int rotation);
/**
- * Release the orientation lock imposed by freezeRotation().
- * @hide
+ * Equivalent to calling {@link #thawDisplayRotation(int)} with {@link
+ * android.view.Display#DEFAULT_DISPLAY}.
*/
void thawRotation();
/**
- * Gets whether the rotation is frozen.
- *
- * @return Whether the rotation is frozen.
+ * Equivelant to call {@link #isDisplayRotationFrozen(int)} with {@link
+ * android.view.Display#DEFAULT_DISPLAY}.
*/
boolean isRotationFrozen();
/**
+ * Lock the display orientation to the specified rotation, or to the current
+ * rotation if -1. Sensor input will be ignored until thawRotation() is called.
+ *
+ * @param displayId the ID of display which rotation should be frozen.
+ * @param rotation one of {@link android.view.Surface#ROTATION_0},
+ * {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180},
+ * {@link android.view.Surface#ROTATION_270} or -1 to freeze it to current rotation.
+ * @hide
+ */
+ void freezeDisplayRotation(int displayId, int rotation);
+
+ /**
+ * Release the orientation lock imposed by freezeRotation() on the display.
+ *
+ * @param displayId the ID of display which rotation should be thawed.
+ * @hide
+ */
+ void thawDisplayRotation(int displayId);
+
+ /**
+ * Gets whether the rotation is frozen on the display.
+ *
+ * @param displayId the ID of display which frozen is needed.
+ * @return Whether the rotation is frozen.
+ */
+ boolean isDisplayRotationFrozen(int displayId);
+
+ /**
* Screenshot the current wallpaper layer, including the whole screen.
*/
Bitmap screenshotWallpaper();
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 29c58dc..b59d8c7 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1776,6 +1776,47 @@
static public MotionEvent obtain(long downTime, long eventTime, int action,
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
+ return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ DEFAULT_DISPLAY);
+ }
+
+ /**
+ * Create a new MotionEvent, filling in all of the basic values that
+ * define the motion.
+ *
+ * @param downTime The time (in ms) when the user originally pressed down to start
+ * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param eventTime The the time (in ms) when this specific event was generated. This
+ * must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
+ * @param x The X coordinate of this event.
+ * @param y The Y coordinate of this event.
+ * @param pressure The current pressure of this event. The pressure generally
+ * ranges from 0 (no pressure at all) to 1 (normal pressure), however
+ * values higher than 1 may be generated depending on the calibration of
+ * the input device.
+ * @param size A scaled value of the approximate size of the area being pressed when
+ * touched with the finger. The actual value in pixels corresponding to the finger
+ * touch is normalized with a device specific range of values
+ * and scaled to a value between 0 and 1.
+ * @param metaState The state of any meta / modifier keys that were in effect when
+ * the event was generated.
+ * @param xPrecision The precision of the X coordinate being reported.
+ * @param yPrecision The precision of the Y coordinate being reported.
+ * @param deviceId The id for the device that this event came from. An id of
+ * zero indicates that the event didn't come from a physical device; other
+ * numbers are arbitrary and you shouldn't depend on the values.
+ * @param source The source of this event.
+ * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
+ * MotionEvent.
+ * @param displayId The display ID associated with this event.
+ * @hide
+ */
+ public static MotionEvent obtain(long downTime, long eventTime, int action,
+ float x, float y, float pressure, float size, int metaState,
+ float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source,
+ int displayId) {
MotionEvent ev = obtain();
synchronized (gSharedTempLock) {
ensureSharedTempPointerCapacity(1);
@@ -1791,7 +1832,7 @@
pc[0].size = size;
ev.mNativePtr = nativeInitialize(ev.mNativePtr,
- deviceId, InputDevice.SOURCE_UNKNOWN, DEFAULT_DISPLAY,
+ deviceId, source, displayId,
action, 0, edgeFlags, metaState, 0,
0, 0, xPrecision, yPrecision,
downTime * NS_PER_MS, eventTime * NS_PER_MS,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 29d3742..cc58b89 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24192,7 +24192,7 @@
* </ul>
* @return {@code true} if the method completes successfully, or
* {@code false} if it fails anywhere. Returning {@code false} means the system was unable to
- * do a drag, and so no drag operation is in progress.
+ * do a drag because of another ongoing operation or some other reasons.
*/
public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,
Object myLocalState, int flags) {
@@ -24235,51 +24235,51 @@
Log.d(VIEW_LOG_TAG, "drag shadow: width=" + shadowSize.x + " height=" + shadowSize.y
+ " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
}
- if (mAttachInfo.mDragSurface != null) {
- mAttachInfo.mDragSurface.release();
- }
- mAttachInfo.mDragSurface = new Surface();
- mAttachInfo.mDragToken = null;
final ViewRootImpl root = mAttachInfo.mViewRootImpl;
final SurfaceSession session = new SurfaceSession(root.mSurface);
- final SurfaceControl surface = new SurfaceControl.Builder(session)
+ final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
.setName("drag surface")
.setSize(shadowSize.x, shadowSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
+ final Surface surface = new Surface();
+ surface.copyFrom(surfaceControl);
+ IBinder token = null;
try {
- mAttachInfo.mDragSurface.copyFrom(surface);
- final Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null);
+ final Canvas canvas = surface.lockCanvas(null);
try {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
shadowBuilder.onDrawShadow(canvas);
} finally {
- mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas);
+ surface.unlockCanvasAndPost(canvas);
}
- // Cache the local state object for delivery with DragEvents
- root.setLocalDragState(myLocalState);
-
// repurpose 'shadowSize' for the last touch point
root.getLastTouchPoint(shadowSize);
- mAttachInfo.mDragToken = mAttachInfo.mSession.performDrag(
- mAttachInfo.mWindow, flags, surface, root.getLastTouchSource(),
+ token = mAttachInfo.mSession.performDrag(
+ mAttachInfo.mWindow, flags, surfaceControl, root.getLastTouchSource(),
shadowSize.x, shadowSize.y, shadowTouchPoint.x, shadowTouchPoint.y, data);
if (ViewDebug.DEBUG_DRAG) {
- Log.d(VIEW_LOG_TAG, "performDrag returned " + mAttachInfo.mDragToken);
+ Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
}
-
- return mAttachInfo.mDragToken != null;
+ if (token != null) {
+ if (mAttachInfo.mDragSurface != null) {
+ mAttachInfo.mDragSurface.release();
+ }
+ mAttachInfo.mDragSurface = surface;
+ mAttachInfo.mDragToken = token;
+ // Cache the local state object for delivery with DragEvents
+ root.setLocalDragState(myLocalState);
+ }
+ return token != null;
} catch (Exception e) {
Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
return false;
} finally {
- if (mAttachInfo.mDragToken == null) {
- mAttachInfo.mDragSurface.destroy();
- mAttachInfo.mDragSurface = null;
- root.setLocalDragState(null);
+ if (token == null) {
+ surface.destroy();
}
session.kill();
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b3e62d..58febb05 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -541,11 +541,11 @@
private static final int CHILD_TOP_INDEX = 1;
// Child views of this ViewGroup
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private View[] mChildren;
// Number of valid children in the mChildren array, the rest should be null or not
// considered as children
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private int mChildrenCount;
// Whether layout calls are currently being suppressed, controlled by calls to
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 982737a..c1e94d8 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1085,6 +1085,19 @@
*
* <p>Refer to the individual flags for the permissions needed.
*
+ * @param flags The flag bits to add.
+ *
+ * @hide
+ */
+ public void addPrivateFlags(int flags) {
+ setPrivateFlags(flags, flags);
+ }
+
+ /**
+ * Add system flag bits.
+ *
+ * <p>Refer to the individual flags for the permissions needed.
+ *
* <p>Note: Only for updateable system components (aka. mainline modules)
*
* @param flags The flag bits to add.
@@ -1092,8 +1105,8 @@
* @hide
*/
@SystemApi
- public void addPrivateFlags(int flags) {
- setPrivateFlags(flags, flags);
+ public void addSystemFlags(@WindowManager.LayoutParams.SystemFlags int flags) {
+ addPrivateFlags(flags);
}
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 742df5e8..2d77cb4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1670,7 +1670,7 @@
*/
@SystemApi
@RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
- public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
+ public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
/**
* Indicates that this window is the rounded corners overlay present on some
@@ -1708,6 +1708,18 @@
public static final int PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION = 0x00800000;
/**
+ * An internal annotation for flags that can be specified to {@link #softInputMode}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ })
+ public @interface SystemFlags {}
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
@@ -1781,8 +1793,8 @@
equals = PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
name = "SUSTAINED_PERFORMANCE_MODE"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
- equals = PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ mask = SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ equals = SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
name = "HIDE_NON_SYSTEM_OVERLAY_WINDOWS"),
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ca2ccaf..e370e11 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -48,6 +48,7 @@
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
+import android.view.Display;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventSender;
@@ -58,7 +59,10 @@
import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
@@ -66,7 +70,6 @@
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -265,7 +268,7 @@
* @hide
*/
public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() {
- getInstanceInternal();
+ forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper());
}
private static final Object sLock = new Object();
@@ -279,6 +282,17 @@
static InputMethodManager sInstance;
/**
+ * Global map between display to {@link InputMethodManager}.
+ *
+ * <p>Currently this map works like a so-called leaky singleton. Once an instance is registered
+ * for the associated display ID, that instance will never be garbage collected.</p>
+ *
+ * <p>TODO(Bug 116699479): Implement instance clean up mechanism.</p>
+ */
+ @GuardedBy("sLock")
+ private static final SparseArray<InputMethodManager> sInstanceMap = new SparseArray<>();
+
+ /**
* @hide Flag for IInputMethodManager.windowGainedFocus: a view in
* the window has input focus.
*/
@@ -335,6 +349,8 @@
// Our generic input connection if the current target does not have its own.
final IInputContext mIInputContext;
+ private final int mDisplayId;
+
/**
* True if this input method client is active, initially false.
*/
@@ -452,6 +468,29 @@
return afm != null && afm.isAutofillUiShowing();
}
+ /**
+ * Checks the consistency between {@link InputMethodManager} state and {@link View} state.
+ *
+ * @param view {@link View} to be checked
+ * @return {@code true} if {@code view} is not {@code null} and there is a {@link Context}
+ * mismatch between {@link InputMethodManager} and {@code view}
+ */
+ private boolean shouldDispatchToViewContext(@Nullable View view) {
+ if (view == null) {
+ return false;
+ }
+ final int viewDisplayId = view.getContext().getDisplayId();
+ if (viewDisplayId != mDisplayId) {
+ Log.w(TAG, "b/117267690: Context mismatch found. view=" + view + " belongs to"
+ + " displayId=" + viewDisplayId
+ + " but InputMethodManager belongs to displayId=" + mDisplayId
+ + ". Use the right InputMethodManager instance to avoid performance overhead.",
+ new Throwable());
+ return true;
+ }
+ return false;
+ }
+
private static boolean canStartInput(View servedView) {
// We can start input ether the servedView has window focus
// or the activity is showing autofill ui.
@@ -503,17 +542,16 @@
mCurId = res.id;
mBindSequence = res.sequence;
}
- startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
- null, 0, 0, 0);
+ startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
}
case MSG_UNBIND: {
final int sequence = msg.arg1;
- @InputMethodClient.UnbindReason
+ @UnbindReason
final int reason = msg.arg2;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
- " reason=" + InputMethodClient.getUnbindReason(reason));
+ " reason=" + InputMethodDebug.unbindReasonToString(reason));
}
final boolean startInput;
synchronized (mH) {
@@ -530,8 +568,7 @@
}
if (startInput) {
startInputInner(
- InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0,
- 0);
+ StartInputReason.UNBOUND_FROM_IMMS, null, 0, 0, 0);
}
return;
}
@@ -560,9 +597,8 @@
// handling this message.
if (mServedView != null && canStartInput(mServedView)) {
if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
- final int reason = active ?
- InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
- InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
+ final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+ : StartInputReason.DEACTIVATED_BY_IMMS;
startInputInner(reason, null, 0, 0, 0);
}
}
@@ -659,7 +695,7 @@
}
@Override
- public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
+ public void onUnbindMethod(int sequence, @UnbindReason int unbindReason) {
mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
}
@@ -733,33 +769,52 @@
});
}
- InputMethodManager(Looper looper) throws ServiceNotFoundException {
+ InputMethodManager(int displayId, Looper looper) throws ServiceNotFoundException {
mService = getIInputMethodManager();
mMainLooper = looper;
mH = new H(looper);
+ mDisplayId = displayId;
mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this);
}
/**
- * Retrieve the global {@link InputMethodManager} instance, creating it if it doesn't already
- * exist.
+ * Retrieve an instance for the given {@link Context}, creating it if it doesn't already exist.
*
- * @return global {@link InputMethodManager} instance
+ * @param context {@link Context} for which IME APIs need to work
+ * @return {@link InputMethodManager} instance
* @hide
*/
- public static InputMethodManager getInstanceInternal() {
+ @Nullable
+ public static InputMethodManager forContext(Context context) {
+ final int displayId = context.getDisplayId();
+ // For better backward compatibility, we always use Looper.getMainLooper() for the default
+ // display case.
+ final Looper looper = displayId == Display.DEFAULT_DISPLAY
+ ? Looper.getMainLooper() : context.getMainLooper();
+ return forContextInternal(displayId, looper);
+ }
+
+ @Nullable
+ private static InputMethodManager forContextInternal(int displayId, Looper looper) {
+ final boolean isDefaultDisplay = displayId == Display.DEFAULT_DISPLAY;
synchronized (sLock) {
- if (sInstance == null) {
- try {
- final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper());
- imm.mService.addClient(imm.mClient, imm.mIInputContext);
- sInstance = imm;
- } catch (ServiceNotFoundException | RemoteException e) {
- throw new IllegalStateException(e);
- }
+ InputMethodManager instance = sInstanceMap.get(displayId);
+ if (instance != null) {
+ return instance;
}
- return sInstance;
+ try {
+ instance = new InputMethodManager(displayId, looper);
+ instance.mService.addClient(instance.mClient, instance.mIInputContext, displayId);
+ } catch (ServiceNotFoundException | RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ // For backward compatibility, store the instance also to sInstance for default display.
+ if (sInstance == null && isDefaultDisplay) {
+ sInstance = instance;
+ }
+ sInstanceMap.put(displayId, instance);
+ return instance;
}
}
@@ -916,6 +971,11 @@
* input method.
*/
public boolean isActive(View view) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ return view.getContext().getSystemService(InputMethodManager.class).isActive(view);
+ }
+
checkFocus();
synchronized (mH) {
return (mServedView == view
@@ -991,13 +1051,6 @@
mNextServedView = null;
if (mServedView != null) {
if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
- if (mCurrentTextBoxAttribute != null) {
- try {
- mService.finishInput(mClient);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
mServedView = null;
mCompletions = null;
mServedConnecting = false;
@@ -1006,6 +1059,13 @@
}
public void displayCompletions(View view, CompletionInfo[] completions) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class)
+ .displayCompletions(view, completions);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if (mServedView != view && (mServedView == null
@@ -1024,6 +1084,13 @@
}
public void updateExtractedText(View view, int token, ExtractedText text) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class)
+ .updateExtractedText(view, token, text);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if (mServedView != view && (mServedView == null
@@ -1065,6 +1132,12 @@
* 0 or have the {@link #SHOW_IMPLICIT} bit set.
*/
public boolean showSoftInput(View view, int flags) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ return view.getContext().getSystemService(InputMethodManager.class)
+ .showSoftInput(view, flags);
+ }
+
return showSoftInput(view, flags, null);
}
@@ -1127,6 +1200,12 @@
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ return view.getContext().getSystemService(InputMethodManager.class)
+ .showSoftInput(view, flags, resultReceiver);
+ }
+
checkFocus();
synchronized (mH) {
if (mServedView != view && (mServedView == null
@@ -1290,6 +1369,12 @@
* @param view The view whose text has changed.
*/
public void restartInput(View view) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class).restartInput(view);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if (mServedView != view && (mServedView == null
@@ -1300,11 +1385,10 @@
mServedConnecting = true;
}
- startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0,
- 0, 0);
+ startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0);
}
- boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
+ boolean startInputInner(@StartInputReason int startInputReason,
@Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
final View view;
@@ -1314,7 +1398,7 @@
// Make sure we have a window token for the served view.
if (DEBUG) {
Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
- " reason=" + InputMethodClient.getStartInputReason(startInputReason));
+ " reason=" + InputMethodDebug.startInputReasonToString(startInputReason));
}
if (view == null) {
if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
@@ -1433,7 +1517,7 @@
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
+ " null. startInputReason="
- + InputMethodClient.getStartInputReason(startInputReason)
+ + InputMethodDebug.startInputReasonToString(startInputReason)
+ " editorInfo=" + tba
+ " controlFlags=#" + Integer.toHexString(controlFlags));
return false;
@@ -1568,7 +1652,7 @@
@UnsupportedAppUsage
public void checkFocus() {
if (checkFocusNoStartInput(false)) {
- startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
+ startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
}
}
@@ -1631,7 +1715,7 @@
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
- + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+ + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " first=" + first + " flags=#"
+ Integer.toHexString(windowFlags));
if (mRestartOnNextWindowFocus) {
@@ -1658,8 +1742,8 @@
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
// smooth.
- if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
- rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
+ if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
+ controlFlags, softInputMode, windowFlags)) {
return;
}
}
@@ -1670,7 +1754,7 @@
try {
if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
mService.startInputOrWindowGainedFocus(
- InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
null, 0 /* missingMethodFlags */,
rootView.getContext().getApplicationInfo().targetSdkVersion);
@@ -1714,6 +1798,13 @@
*/
public void updateSelection(View view, int selStart, int selEnd,
int candidatesStart, int candidatesEnd) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class)
+ .updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if ((mServedView != view && (mServedView == null
@@ -1751,6 +1842,12 @@
* Notify the event when the user tapped or clicked the text view.
*/
public void viewClicked(View view) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class).viewClicked(view);
+ return;
+ }
+
final boolean focusChanged = mServedView != mNextServedView;
checkFocus();
synchronized (mH) {
@@ -1815,6 +1912,13 @@
*/
@Deprecated
public void updateCursor(View view, int left, int top, int right, int bottom) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class)
+ .updateCursor(view, left, top, right, bottom);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if ((mServedView != view && (mServedView == null
@@ -1846,6 +1950,13 @@
if (view == null || cursorAnchorInfo == null) {
return;
}
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class)
+ .updateCursorAnchorInfo(view, cursorAnchorInfo);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if ((mServedView != view &&
@@ -1891,6 +2002,13 @@
* @param data Any data to include with the command.
*/
public void sendAppPrivateCommand(View view, String action, Bundle data) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(view)) {
+ view.getContext().getSystemService(InputMethodManager.class)
+ .sendAppPrivateCommand(view, action, data);
+ return;
+ }
+
checkFocus();
synchronized (mH) {
if ((mServedView != view && (mServedView == null
@@ -2062,6 +2180,13 @@
*/
public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
@NonNull KeyEvent event) {
+ // Re-dispatch if there is a context mismatch.
+ if (shouldDispatchToViewContext(targetView)) {
+ targetView.getContext().getSystemService(InputMethodManager.class)
+ .dispatchKeyEventFromInputMethod(targetView, event);
+ return;
+ }
+
synchronized (mH) {
ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
if (viewRootImpl == null) {
@@ -2551,6 +2676,7 @@
sb.append(",windowFocus=" + view.hasWindowFocus());
sb.append(",autofillUiShowing=" + isAutofillUIShowing(view));
sb.append(",window=" + view.getWindowToken());
+ sb.append(",displayId=" + view.getContext().getDisplayId());
sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
return sb.toString();
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9692579..2e92f14 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.os.LocaleList;
import android.os.Looper;
@@ -212,34 +211,13 @@
return suggestSelection(request);
}
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- @UnsupportedAppUsage
- default TextSelection suggestSelection(
- @NonNull CharSequence text,
- @IntRange(from = 0) int selectionStartIndex,
- @IntRange(from = 0) int selectionEndIndex,
- @Nullable TextSelection.Options options) {
- if (options == null) {
- return suggestSelection(new TextSelection.Request.Builder(
- text, selectionStartIndex, selectionEndIndex).build());
- } else if (options.getRequest() != null) {
- return suggestSelection(options.getRequest());
- } else {
- return suggestSelection(
- new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex)
- .setDefaultLocales(options.getDefaultLocales())
- .build());
- }
- }
-
/**
* Classifies the specified text and returns a {@link TextClassification} object that can be
* used to generate a widget for handling the classified text.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @param request the text classification request
@@ -262,7 +240,7 @@
* {@link #classifyText(TextClassification.Request)}. If that method calls this method,
* a stack overflow error will happen.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @param text text providing context for the text to classify (which is specified
@@ -292,34 +270,13 @@
return classifyText(request);
}
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- @UnsupportedAppUsage
- default TextClassification classifyText(
- @NonNull CharSequence text,
- @IntRange(from = 0) int startIndex,
- @IntRange(from = 0) int endIndex,
- @Nullable TextClassification.Options options) {
- if (options == null) {
- return classifyText(
- new TextClassification.Request.Builder(text, startIndex, endIndex).build());
- } else if (options.getRequest() != null) {
- return classifyText(options.getRequest());
- } else {
- return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex)
- .setDefaultLocales(options.getDefaultLocales())
- .setReferenceTime(options.getReferenceTime())
- .build());
- }
- }
-
/**
* Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
* links information.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @param request the text links request
@@ -334,27 +291,10 @@
return new TextLinks.Builder(request.getText().toString()).build();
}
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- @UnsupportedAppUsage
- default TextLinks generateLinks(
- @NonNull CharSequence text, @Nullable TextLinks.Options options) {
- if (options == null) {
- return generateLinks(new TextLinks.Request.Builder(text).build());
- } else if (options.getRequest() != null) {
- return generateLinks(options.getRequest());
- } else {
- return generateLinks(new TextLinks.Request.Builder(text)
- .setDefaultLocales(options.getDefaultLocales())
- .setEntityConfig(options.getEntityConfig())
- .build());
- }
- }
-
/**
* Returns the maximal length of text that can be processed by generateLinks.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @see #generateLinks(TextLinks.Request)
@@ -365,9 +305,29 @@
}
/**
+ * Detects the language of the specified text.
+ *
+ * <p><strong>NOTE: </strong>Call on a worker thread.
+ *
+ *
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+ *
+ * @param request the {@link TextLanguage} request.
+ * @return the {@link TextLanguage} result.
+ */
+ @WorkerThread
+ @NonNull
+ default TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
+ Preconditions.checkNotNull(request);
+ Utils.checkMainThread();
+ return TextLanguage.EMPTY;
+ }
+
+ /**
* Reports a selection event.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*/
default void onSelectionEvent(@NonNull SelectionEvent event) {}
@@ -375,7 +335,7 @@
/**
* Destroys this TextClassifier.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* <p>Subsequent calls to this method are no-ops.
@@ -385,7 +345,7 @@
/**
* Returns whether or not this TextClassifier has been destroyed.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
* with the classifier and an attempt to do so would throw an {@link IllegalStateException}.
* However, this method should never throw an {@link IllegalStateException}.
*
@@ -396,9 +356,7 @@
}
/** @hide **/
- default void dump(@NonNull IndentingPrintWriter printWriter) {
-
- }
+ default void dump(@NonNull IndentingPrintWriter printWriter) {}
/**
* Configuration object for specifying what entities to identify.
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
new file mode 100644
index 0000000..d28459e
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -0,0 +1,307 @@
+/*
+ * 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.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Represents the result of language detection of a piece of text.
+ * <p>
+ * This contains a list of locales, each paired with a confidence score, sorted in decreasing
+ * order of those scores. E.g., for a given input text, the model may return
+ * {@code [<"en", 0.85>, <"fr", 0.15>]}. This sample result means the model reports that it is
+ * 85% likely that the entire text is in English and 15% likely that the entire text is in French,
+ * etc. It does not mean that 85% of the input is in English and 15% is in French.
+ */
+public final class TextLanguage implements Parcelable {
+
+ public static final Creator<TextLanguage> CREATOR = new Creator<TextLanguage>() {
+ @Override
+ public TextLanguage createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public TextLanguage[] newArray(int size) {
+ return new TextLanguage[size];
+ }
+ };
+
+ static final TextLanguage EMPTY = new Builder().build();
+
+ @Nullable private final String mId;
+ private final EntityConfidence mEntityConfidence;
+ private final Bundle mBundle;
+
+ private TextLanguage(
+ @Nullable String id,
+ EntityConfidence entityConfidence,
+ Bundle bundle) {
+ mId = id;
+ mEntityConfidence = entityConfidence;
+ mBundle = bundle;
+ }
+
+ /**
+ * Returns the id, if one exists, for this object.
+ */
+ @Nullable
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the number of possible locales for the processed text.
+ */
+ @IntRange(from = 0)
+ public int getLocaleHypothesisCount() {
+ return mEntityConfidence.getEntities().size();
+ }
+
+ /**
+ * Returns the language locale at the specified index. Locales are ordered from high
+ * confidence to low confidence.
+ *
+ * @throws IndexOutOfBoundsException if the specified index is out of range.
+ * @see #getLocaleCount() for the number of locales available.
+ */
+ @NonNull
+ public ULocale getLocale(int index) {
+ return ULocale.forLanguageTag(mEntityConfidence.getEntities().get(index));
+ }
+
+ /**
+ * Returns the confidence score for the specified language locale. The value ranges from
+ * 0 (low confidence) to 1 (high confidence). 0 indicates that the locale was not found for
+ * the processed text.
+ */
+ @FloatRange(from = 0.0, to = 1.0)
+ public float getConfidenceScore(@NonNull ULocale locale) {
+ return mEntityConfidence.getConfidenceScore(locale.toLanguageTag());
+ }
+
+ /**
+ * Returns a bundle containing non-structured extra information about this result.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should prefer
+ * to hold a reference to the returned bundle rather than frequently calling this method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mBundle.deepCopy();
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "TextLanguage {id=%s, locales=%s, bundle=%s}",
+ mId, mEntityConfidence, mBundle);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ mEntityConfidence.writeToParcel(dest, flags);
+ dest.writeBundle(mBundle);
+ }
+
+ private static TextLanguage readFromParcel(Parcel in) {
+ return new TextLanguage(
+ in.readString(),
+ EntityConfidence.CREATOR.createFromParcel(in),
+ in.readBundle());
+ }
+
+ /**
+ * Builder used to build TextLanguage objects.
+ */
+ public static final class Builder {
+
+ @Nullable private String mId;
+ private final Map<String, Float> mEntityConfidenceMap = new ArrayMap<>();
+ @Nullable private Bundle mBundle;
+
+ /**
+ * Sets a language locale for the processed text and assigns a confidence score. If the
+ * locale has already been set, this updates it.
+ *
+ * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+ * 0 implies the locale does not exist for the processed text.
+ * Values greater than 1 are clamped to 1.
+ */
+ @NonNull
+ public Builder putLocale(
+ @NonNull ULocale locale,
+ @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+ Preconditions.checkNotNull(locale);
+ mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
+ return this;
+ }
+
+ /**
+ * Sets an optional id for the TextLanguage object.
+ */
+ @NonNull
+ public Builder setId(@Nullable String id) {
+ mId = id;
+ return this;
+ }
+
+ /**
+ * Sets a bundle containing non-structured extra information about the TextLanguage object.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle bundle) {
+ mBundle = Preconditions.checkNotNull(bundle);
+ return this;
+ }
+
+ /**
+ * Builds and returns a new TextLanguage object.
+ * <p>
+ * If necessary, this method will verify fields, clamp them, and make them immutable.
+ */
+ @NonNull
+ public TextLanguage build() {
+ mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+ return new TextLanguage(
+ mId,
+ new EntityConfidence(mEntityConfidenceMap),
+ mBundle);
+ }
+ }
+
+ /**
+ * A request object for detecting the language of a piece of text.
+ */
+ public static final class Request implements Parcelable {
+
+ public static final Creator<Request> CREATOR = new Creator<Request>() {
+ @Override
+ public Request createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public Request[] newArray(int size) {
+ return new Request[size];
+ }
+ };
+
+ private final CharSequence mText;
+ private final Bundle mBundle;
+
+ private Request(CharSequence text, Bundle bundle) {
+ mText = text;
+ mBundle = bundle;
+ }
+
+ /**
+ * Returns the text to process.
+ */
+ @NonNull
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns a bundle containing non-structured extra information about this request.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mBundle.deepCopy();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mText);
+ dest.writeBundle(mBundle);
+ }
+
+ private static Request readFromParcel(Parcel in) {
+ return new Request(
+ in.readCharSequence(),
+ in.readBundle());
+ }
+
+ /**
+ * A builder for building TextLanguage requests.
+ */
+ public static final class Builder {
+
+ private final CharSequence mText;
+ @Nullable private Bundle mBundle;
+
+ /**
+ * Creates a builder to build TextLanguage requests.
+ *
+ * @param text the text to process.
+ */
+ public Builder(@NonNull CharSequence text) {
+ mText = Preconditions.checkNotNull(text);
+ }
+
+ /**
+ * Sets a bundle containing non-structured extra information about the request.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle bundle) {
+ mBundle = Preconditions.checkNotNull(bundle);
+ return this;
+ }
+
+ /**
+ * Builds and returns a new TextLanguage request object.
+ * <p>
+ * If necessary, this method will verify fields, clamp them, and make them immutable.
+ */
+ @NonNull
+ public Request build() {
+ mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+ return new Request(mText.toString(), mBundle);
+ }
+ }
+ }
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index bdd7a09..300bb6f 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -282,19 +282,28 @@
SAFE_BROWSING_THREAT_UNKNOWN,
SAFE_BROWSING_THREAT_MALWARE,
SAFE_BROWSING_THREAT_PHISHING,
- SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
+ SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE,
+ SAFE_BROWSING_THREAT_BILLING,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SafeBrowsingThreat {}
- /** The resource was blocked for an unknown reason */
+ /** The resource was blocked for an unknown reason. */
public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0;
- /** The resource was blocked because it contains malware */
+ /** The resource was blocked because it contains malware. */
public static final int SAFE_BROWSING_THREAT_MALWARE = 1;
- /** The resource was blocked because it contains deceptive content */
+ /** The resource was blocked because it contains deceptive content. */
public static final int SAFE_BROWSING_THREAT_PHISHING = 2;
- /** The resource was blocked because it contains unwanted software */
+ /** The resource was blocked because it contains unwanted software. */
public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 3;
+ /**
+ * The resource was blocked because it may trick the user into a billing agreement.
+ *
+ * <p>This constant is only used when targetSdkVersion is at least {@link
+ * android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used
+ * instead.
+ */
+ public static final int SAFE_BROWSING_THREAT_BILLING = 4;
/**
* Report an error to the host application. These errors are unrecoverable
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 35ff6cc..4d03123 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -42,6 +42,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.graphics.drawable.RippleDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -152,6 +153,7 @@
private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
private static final int LAYOUT_PARAM_ACTION_TAG = 19;
private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
+ private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
/**
* Application that hosts the remote views.
@@ -368,10 +370,12 @@
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
ActivityOptions opts = getActivityOptions(context);
+ // The NEW_TASK flags are applied through the activity options and not as a part of
+ // the call to startIntentSender() to ensure that they are consistently applied to
+ // both mutable and immutable PendingIntents.
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
- Intent.FLAG_ACTIVITY_NEW_TASK,
- Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
+ 0, 0, 0, opts.toBundle());
} catch (IntentSender.SendIntentException e) {
android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
return false;
@@ -399,10 +403,15 @@
windowAnimationStyle.recycle();
if (enterAnimationId != 0) {
- return ActivityOptions.makeCustomAnimation(context, enterAnimationId, 0);
+ final ActivityOptions opts = ActivityOptions.makeCustomAnimation(context,
+ enterAnimationId, 0);
+ opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return opts;
}
}
- return ActivityOptions.makeBasic();
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return opts;
}
}
@@ -1122,6 +1131,53 @@
PorterDuff.Mode filterMode;
}
+ /**
+ * Equivalent to calling
+ * {@link RippleDrawable#setColor(ColorStateList)},
+ * on the {@link Drawable} of a given view.
+ * <p>
+ * The operation will be performed on the {@link Drawable} returned by the
+ * target {@link View#getBackground()}.
+ * <p>
+ */
+ private class SetRippleDrawableColor extends Action {
+
+ ColorStateList mColorStateList;
+
+ SetRippleDrawableColor(int id, ColorStateList colorStateList) {
+ this.viewId = id;
+ this.mColorStateList = colorStateList;
+ }
+
+ SetRippleDrawableColor(Parcel parcel) {
+ viewId = parcel.readInt();
+ mColorStateList = parcel.readParcelable(null);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeParcelable(mColorStateList, 0);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ // Pick the correct drawable to modify for this view
+ Drawable targetDrawable = target.getBackground();
+
+ if (targetDrawable instanceof RippleDrawable) {
+ ((RippleDrawable) targetDrawable.mutate()).setColor(mColorStateList);
+ }
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_RIPPLE_DRAWABLE_COLOR_TAG;
+ }
+ }
+
private final class ViewContentNavigation extends Action {
final boolean mNext;
@@ -2394,6 +2450,8 @@
return new LayoutParamAction(parcel);
case OVERRIDE_TEXT_COLORS_TAG:
return new OverrideTextColorsAction(parcel);
+ case SET_RIPPLE_DRAWABLE_COLOR_TAG:
+ return new SetRippleDrawableColor(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -2855,6 +2913,22 @@
/**
* @hide
+ * Equivalent to calling
+ * {@link RippleDrawable#setColor(ColorStateList)} on the {@link Drawable} of a given view,
+ * assuming it's a {@link RippleDrawable}.
+ * <p>
+ *
+ * @param viewId The id of the view that contains the target
+ * {@link RippleDrawable}
+ * @param colorStateList Specify a color for a
+ * {@link ColorStateList} for this drawable.
+ */
+ public void setRippleDrawableColor(int viewId, ColorStateList colorStateList) {
+ addAction(new SetRippleDrawableColor(viewId, colorStateList));
+ }
+
+ /**
+ * @hide
* Equivalent to calling {@link android.widget.ProgressBar#setProgressTintList}.
*
* @param viewId The id of the view whose tint should change
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f74c234..66809db 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -63,7 +63,7 @@
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
-import android.graphics.fonts.Font;
+import android.graphics.fonts.FontStyle;
import android.graphics.fonts.FontVariationAxis;
import android.icu.text.DecimalFormatSymbols;
import android.os.AsyncTask;
@@ -2073,7 +2073,7 @@
*/
private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
@XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
- @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
+ @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int weight) {
if (typeface == null && familyName != null) {
// Lookup normal Typeface from system font map.
final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
@@ -2100,9 +2100,9 @@
}
private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
- @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
+ @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int weight) {
if (weight >= 0) {
- weight = Math.min(Font.FONT_WEIGHT_MAX, weight);
+ weight = Math.min(FontStyle.FONT_WEIGHT_MAX, weight);
final boolean italic = (style & Typeface.ITALIC) != 0;
setTypeface(Typeface.create(typeface, weight, italic));
} else {
@@ -3517,6 +3517,7 @@
ColorStateList mTextColorHint = null;
ColorStateList mTextColorLink = null;
int mTextSize = -1;
+ LocaleList mTextLocales = null;
String mFontFamily = null;
Typeface mFontTypeface = null;
boolean mFontFamilyExplicit = false;
@@ -3543,6 +3544,7 @@
+ " mTextColorHint:" + mTextColorHint + "\n"
+ " mTextColorLink:" + mTextColorLink + "\n"
+ " mTextSize:" + mTextSize + "\n"
+ + " mTextLocales:" + mTextLocales + "\n"
+ " mFontFamily:" + mFontFamily + "\n"
+ " mFontTypeface:" + mFontTypeface + "\n"
+ " mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
@@ -3579,6 +3581,8 @@
com.android.internal.R.styleable.TextAppearance_textColorLink);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_textSize,
com.android.internal.R.styleable.TextAppearance_textSize);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_textLocale,
+ com.android.internal.R.styleable.TextAppearance_textLocale);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_typeface,
com.android.internal.R.styleable.TextAppearance_typeface);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFamily,
@@ -3652,6 +3656,15 @@
attributes.mTextSize =
appearance.getDimensionPixelSize(attr, attributes.mTextSize);
break;
+ case com.android.internal.R.styleable.TextAppearance_textLocale:
+ final String localeString = appearance.getString(attr);
+ if (localeString != null) {
+ final LocaleList localeList = LocaleList.forLanguageTags(localeString);
+ if (!localeList.isEmpty()) {
+ attributes.mTextLocales = localeList;
+ }
+ }
+ break;
case com.android.internal.R.styleable.TextAppearance_typeface:
attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex);
if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
@@ -3738,6 +3751,10 @@
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
+ if (attributes.mTextLocales != null) {
+ setTextLocales(attributes.mTextLocales);
+ }
+
if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
attributes.mFontFamily = null;
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 10cf702..c256d57 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -137,7 +137,7 @@
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
- final int displayId = mContext.getDisplay().getDisplayId();
+ final int displayId = mContext.getDisplayId();
try {
service.enqueueToast(pkg, tn, mDuration, displayId);
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 0f8295a..7c371cb 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -31,6 +31,8 @@
import android.provider.Settings;
import android.util.Log;
+import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.Set;
@@ -42,6 +44,14 @@
private static final String TAG = "AssistUtils";
+ /**
+ * Sentinel value for "no default assistant specified."
+ *
+ * Empty string is already used to represent an explicit setting of No Assistant. null cannot
+ * be used because we can't represent a null value in XML.
+ */
+ private static final String UNSET = "#+UNSET";
+
private final Context mContext;
private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
@@ -178,10 +188,21 @@
return ComponentName.unflattenFromString(setting);
}
+ final String defaultSetting = mContext.getResources().getString(
+ R.string.config_defaultAssistantComponentName);
+ if (defaultSetting != null && !defaultSetting.equals(UNSET)) {
+ return ComponentName.unflattenFromString(defaultSetting);
+ }
+
// Fallback to keep backward compatible behavior when there is no user setting.
if (activeServiceSupportsAssistGesture()) {
return getActiveServiceComponentName();
}
+
+ if (UNSET.equals(defaultSetting)) {
+ return null;
+ }
+
final SearchManager searchManager =
(SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
if (searchManager == null) {
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index a8edfb6..498de53 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -16,12 +16,17 @@
package com.android.internal.app;
+import static android.content.res.ResourceId.ID_NULL;
+
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Slog;
@@ -31,16 +36,19 @@
public class SuspendedAppActivity extends AlertActivity
implements DialogInterface.OnClickListener {
- private static final String TAG = "SuspendedAppActivity";
- public static final String EXTRA_SUSPENDED_PACKAGE =
- "SuspendedAppActivity.extra.SUSPENDED_PACKAGE";
+ private static final String TAG = SuspendedAppActivity.class.getSimpleName();
+ private static final String PACKAGE_NAME = "com.android.internal.app";
+
+ public static final String EXTRA_SUSPENDED_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDED_PACKAGE";
public static final String EXTRA_SUSPENDING_PACKAGE =
- "SuspendedAppActivity.extra.SUSPENDING_PACKAGE";
- public static final String EXTRA_DIALOG_MESSAGE = "SuspendedAppActivity.extra.DIALOG_MESSAGE";
+ PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE";
+ public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO";
private Intent mMoreDetailsIntent;
private int mUserId;
private PackageManager mPm;
+ private Resources mSuspendingAppResources;
+ private SuspendDialogInfo mSuppliedDialogInfo;
private CharSequence getAppLabel(String packageName) {
try {
@@ -66,6 +74,65 @@
return null;
}
+ private Drawable resolveIcon() {
+ final int iconId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getIconResId()
+ : ID_NULL;
+ if (iconId != ID_NULL && mSuspendingAppResources != null) {
+ try {
+ return mSuspendingAppResources.getDrawable(iconId, null);
+ } catch (Resources.NotFoundException nfe) {
+ Slog.e(TAG, "Could not resolve drawable resource id " + iconId);
+ }
+ }
+ return null;
+ }
+
+ private String resolveTitle() {
+ final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId()
+ : ID_NULL;
+ if (titleId != ID_NULL && mSuspendingAppResources != null) {
+ try {
+ return mSuspendingAppResources.getString(titleId);
+ } catch (Resources.NotFoundException nfe) {
+ Slog.e(TAG, "Could not resolve string resource id " + titleId);
+ }
+ }
+ return getString(R.string.app_suspended_title);
+ }
+
+ private String resolveDialogMessage(String suspendingPkg, String suspendedPkg) {
+ final CharSequence suspendedAppLabel = getAppLabel(suspendedPkg);
+ if (mSuppliedDialogInfo != null) {
+ final int messageId = mSuppliedDialogInfo.getDialogMessageResId();
+ final String message = mSuppliedDialogInfo.getDialogMessage();
+ if (messageId != ID_NULL && mSuspendingAppResources != null) {
+ try {
+ return mSuspendingAppResources.getString(messageId, suspendedAppLabel);
+ } catch (Resources.NotFoundException nfe) {
+ Slog.e(TAG, "Could not resolve string resource id " + messageId);
+ }
+ } else if (message != null) {
+ return String.format(getResources().getConfiguration().getLocales().get(0), message,
+ suspendedAppLabel);
+ }
+ }
+ return getString(R.string.app_suspended_default_message, suspendedAppLabel,
+ getAppLabel(suspendingPkg));
+ }
+
+ private String resolveNeutralButtonText() {
+ final int buttonTextId = (mSuppliedDialogInfo != null)
+ ? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL;
+ if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
+ try {
+ return mSuspendingAppResources.getString(buttonTextId);
+ } catch (Resources.NotFoundException nfe) {
+ Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
+ }
+ }
+ return getString(R.string.app_suspended_more_details);
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -79,26 +146,26 @@
finish();
return;
}
- final String suppliedMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
final String suspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE);
final String suspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE);
- final CharSequence suspendedAppLabel = getAppLabel(suspendedPackage);
- final CharSequence dialogMessage;
- if (suppliedMessage == null) {
- dialogMessage = getString(R.string.app_suspended_default_message, suspendedAppLabel,
- getAppLabel(suspendingPackage));
- } else {
- dialogMessage = String.format(getResources().getConfiguration().getLocales().get(0),
- suppliedMessage, suspendedAppLabel);
+ mSuppliedDialogInfo = intent.getParcelableExtra(EXTRA_DIALOG_INFO);
+ if (mSuppliedDialogInfo != null) {
+ try {
+ mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(suspendingPackage,
+ mUserId);
+ } catch (PackageManager.NameNotFoundException ne) {
+ Slog.e(TAG, "Could not find resources for " + suspendingPackage, ne);
+ }
}
final AlertController.AlertParams ap = mAlertParams;
- ap.mTitle = getString(R.string.app_suspended_title);
- ap.mMessage = dialogMessage;
+ ap.mIcon = resolveIcon();
+ ap.mTitle = resolveTitle();
+ ap.mMessage = resolveDialogMessage(suspendingPackage, suspendedPackage);
ap.mPositiveButtonText = getString(android.R.string.ok);
mMoreDetailsIntent = getMoreDetailsActivity(suspendingPackage, suspendedPackage, mUserId);
if (mMoreDetailsIntent != null) {
- ap.mNeutralButtonText = getString(R.string.app_suspended_more_details);
+ ap.mNeutralButtonText = resolveNeutralButtonText();
}
ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
setupAlert();
@@ -116,11 +183,11 @@
}
public static Intent createSuspendedAppInterceptIntent(String suspendedPackage,
- String suspendingPackage, String dialogMessage, int userId) {
+ String suspendingPackage, SuspendDialogInfo dialogInfo, int userId) {
return new Intent()
.setClassName("android", SuspendedAppActivity.class.getName())
.putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
- .putExtra(EXTRA_DIALOG_MESSAGE, dialogMessage)
+ .putExtra(EXTRA_DIALOG_INFO, dialogInfo)
.putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
.putExtra(Intent.EXTRA_USER_ID, userId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
new file mode 100644
index 0000000..a5dc3d1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+
+/**
+ * Provides useful methods for debugging.
+ */
+public final class InputMethodDebug {
+ /**
+ * Not intended to be instantiated.
+ */
+ private InputMethodDebug() {
+ }
+
+ /**
+ * Converts {@link StartInputReason} to {@link String} for debug logging.
+ *
+ * @param reason integer constant for {@link StartInputReason}.
+ * @return {@link String} message corresponds for the given {@code reason}.
+ */
+ public static String startInputReasonToString(@StartInputReason int reason) {
+ switch (reason) {
+ case StartInputReason.UNSPECIFIED:
+ return "UNSPECIFIED";
+ case StartInputReason.WINDOW_FOCUS_GAIN:
+ return "WINDOW_FOCUS_GAIN";
+ case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY:
+ return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
+ case StartInputReason.APP_CALLED_RESTART_INPUT_API:
+ return "APP_CALLED_RESTART_INPUT_API";
+ case StartInputReason.CHECK_FOCUS:
+ return "CHECK_FOCUS";
+ case StartInputReason.BOUND_TO_IMMS:
+ return "BOUND_TO_IMMS";
+ case StartInputReason.UNBOUND_FROM_IMMS:
+ return "UNBOUND_FROM_IMMS";
+ case StartInputReason.ACTIVATED_BY_IMMS:
+ return "ACTIVATED_BY_IMMS";
+ case StartInputReason.DEACTIVATED_BY_IMMS:
+ return "DEACTIVATED_BY_IMMS";
+ case StartInputReason.SESSION_CREATED_BY_IME:
+ return "SESSION_CREATED_BY_IME";
+ default:
+ return "Unknown=" + reason;
+ }
+ }
+
+ /**
+ * Converts {@link UnbindReason} to {@link String} for debug logging.
+ *
+ * @param reason integer constant for {@link UnbindReason}.
+ * @return {@link String} message corresponds for the given {@code reason}.
+ */
+ public static String unbindReasonToString(@UnbindReason int reason) {
+ switch (reason) {
+ case UnbindReason.UNSPECIFIED:
+ return "UNSPECIFIED";
+ case UnbindReason.SWITCH_CLIENT:
+ return "SWITCH_CLIENT";
+ case UnbindReason.SWITCH_IME:
+ return "SWITCH_IME";
+ case UnbindReason.DISCONNECT_IME:
+ return "DISCONNECT_IME";
+ case UnbindReason.NO_IME:
+ return "NO_IME";
+ case UnbindReason.SWITCH_IME_FAILED:
+ return "SWITCH_IME_FAILED";
+ case UnbindReason.SWITCH_USER:
+ return "SWITCH_USER";
+ default:
+ return "Unknown=" + reason;
+ }
+ }
+
+ /**
+ * Converts {@link SoftInputModeFlags} to {@link String} for debug logging.
+ *
+ * @param softInputMode integer constant for {@link SoftInputModeFlags}.
+ * @return {@link String} message corresponds for the given {@code softInputMode}.
+ */
+ public static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
+ final StringBuilder sb = new StringBuilder();
+ final int state = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ final int adjust = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+ final boolean isForwardNav =
+ (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
+
+ switch (state) {
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
+ sb.append("STATE_UNSPECIFIED");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+ sb.append("STATE_UNCHANGED");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+ sb.append("STATE_HIDDEN");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+ sb.append("STATE_ALWAYS_HIDDEN");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+ sb.append("STATE_VISIBLE");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+ sb.append("STATE_ALWAYS_VISIBLE");
+ break;
+ default:
+ sb.append("STATE_UNKNOWN(");
+ sb.append(state);
+ sb.append(")");
+ break;
+ }
+
+ switch (adjust) {
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
+ sb.append("|ADJUST_UNSPECIFIED");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
+ sb.append("|ADJUST_RESIZE");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN:
+ sb.append("|ADJUST_PAN");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
+ sb.append("|ADJUST_NOTHING");
+ break;
+ default:
+ sb.append("|ADJUST_UNKNOWN(");
+ sb.append(adjust);
+ sb.append(")");
+ break;
+ }
+
+ if (isForwardNav) {
+ // This is a special bit that is set by the system only during the window navigation.
+ sb.append("|IS_FORWARD_NAVIGATION");
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
new file mode 100644
index 0000000..a01c459
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link android.view.inputmethod.InputMethodManager} is calling
+ * {@link com.android.internal.view.IInputMethodManager#startInputOrWindowGainedFocus}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+ StartInputReason.UNSPECIFIED,
+ StartInputReason.WINDOW_FOCUS_GAIN,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY,
+ StartInputReason.APP_CALLED_RESTART_INPUT_API,
+ StartInputReason.CHECK_FOCUS,
+ StartInputReason.BOUND_TO_IMMS,
+ StartInputReason.UNBOUND_FROM_IMMS,
+ StartInputReason.ACTIVATED_BY_IMMS,
+ StartInputReason.DEACTIVATED_BY_IMMS,
+ StartInputReason.SESSION_CREATED_BY_IME})
+public @interface StartInputReason {
+ /**
+ * Reason is not specified.
+ */
+ int UNSPECIFIED = 0;
+ /**
+ * {@link android.view.Window} gained focus and it made the focused {@link android.view.View}
+ * to (re)start a new connection.
+ */
+ int WINDOW_FOCUS_GAIN = 1;
+ /**
+ * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
+ * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
+ * this window focus change event.
+ */
+ int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is
+ * either explicitly called by the application or indirectly called by some Framework class
+ * (e.g. {@link android.widget.EditText}).
+ */
+ int APP_CALLED_RESTART_INPUT_API = 3;
+ /**
+ * {@link android.view.View} requested a new connection because of view focus change.
+ */
+ int CHECK_FOCUS = 4;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#onBindMethod}.
+ */
+ int BOUND_TO_IMMS = 5;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+ */
+ int UNBOUND_FROM_IMMS = 6;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#setActive}.
+ */
+ int ACTIVATED_BY_IMMS = 7;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#setActive}.
+ */
+ int DEACTIVATED_BY_IMMS = 8;
+ /**
+ * {@link com.android.server.inputmethod.InputMethodManagerService} is responding to
+ * {@link com.android.internal.view.IInputSessionCallback#sessionCreated}.
+ */
+ int SESSION_CREATED_BY_IME = 9;
+}
diff --git a/core/java/com/android/internal/inputmethod/UnbindReason.java b/core/java/com/android/internal/inputmethod/UnbindReason.java
new file mode 100644
index 0000000..f0f18f1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/UnbindReason.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link com.android.server.inputmethod.InputMethodManagerService} is
+ * calling {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+ UnbindReason.UNSPECIFIED,
+ UnbindReason.SWITCH_CLIENT,
+ UnbindReason.SWITCH_IME,
+ UnbindReason.DISCONNECT_IME,
+ UnbindReason.NO_IME,
+ UnbindReason.SWITCH_IME_FAILED,
+ UnbindReason.SWITCH_USER})
+public @interface UnbindReason {
+ /**
+ * Reason is not specified.
+ */
+ int UNSPECIFIED = 0;
+ /**
+ * When a new IME client becomes active, the previous IME client will unbound from the current
+ * IME.
+ */
+ int SWITCH_CLIENT = 1;
+ /**
+ * Before a new IME becomes active, the current IME client be unbound from the previous IME.
+ */
+ int SWITCH_IME = 2;
+ /**
+ * When the current IME is disconnected, the current IME client will be unbound.
+ */
+ int DISCONNECT_IME = 3;
+ /**
+ * When the system loses the last enabled IME, the current IME client will be unbound.
+ */
+ int NO_IME = 4;
+ /**
+ * When the system failed to switch to another IME, the current IME client will be unbound.
+ */
+ int SWITCH_IME_FAILED = 5;
+ /**
+ * When a new user becomes foreground, the previous IME client will be unbound from the previous
+ * user's active IME.
+ */
+ int SWITCH_USER = 6;
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 46667d1..2442d0b 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -158,7 +158,7 @@
PROC_WAKELOCKS_FORMAT,
nameStringArray, wlData, null);
- name = nameStringArray[0];
+ name = nameStringArray[0].trim();
count = (int) wlData[1];
if (wakeup_sources) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 8751517..3b7ce0a 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -1387,7 +1387,7 @@
private int getOptionsPanelGravity() {
try {
return WindowManagerHolder.sWindowManager.getPreferredOptionsPanelGravity(
- getContext().getDisplay().getDisplayId());
+ getContext().getDisplayId());
} catch (RemoteException ex) {
Log.e(TAG, "Couldn't getOptionsPanelGravity; using default", ex);
return Gravity.CENTER | Gravity.BOTTOM;
@@ -3642,7 +3642,7 @@
if (!mIsWatching) {
try {
WindowManagerHolder.sWindowManager.watchRotation(this,
- phoneWindow.getContext().getDisplay().getDisplayId());
+ phoneWindow.getContext().getDisplayId());
mHandler = new Handler();
mIsWatching = true;
} catch (RemoteException ex) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 2618356..17b2bc4 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -24,7 +24,6 @@
*/
oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
- // unbindReason corresponds to InputMethodClient.UnbindReason.
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5f1243f..29c55c2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -31,7 +31,8 @@
* applications.
*/
interface IInputMethodManager {
- void addClient(in IInputMethodClient client, in IInputContext inputContext);
+ void addClient(in IInputMethodClient client, in IInputContext inputContext,
+ int untrustedDisplayId);
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
@@ -45,7 +46,6 @@
// Currently there is a bug that aidl doesn't accept List<Parcelable>
List getShortcutInputMethodsAndSubtypes();
- void finishInput(in IInputMethodClient client);
boolean showSoftInput(in IInputMethodClient client, int flags,
in ResultReceiver resultReceiver);
boolean hideSoftInput(in IInputMethodClient client, int flags,
@@ -54,7 +54,7 @@
// has gained focus, and if 'attribute' is non-null then also does startInput.
// @NonNull
InputBindResult startInputOrWindowGainedFocus(
- /* @InputMethodClient.StartInputReason */ int startInputReason,
+ /* @StartInputReason */ int startInputReason,
in IInputMethodClient client, in IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, IInputContext inputContext,
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 101fd41..ec8e8da 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -51,6 +51,9 @@
ResultCode.ERROR_INVALID_USER,
ResultCode.ERROR_NULL_EDITOR_INFO,
ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
+ ResultCode.ERROR_NO_EDITOR,
+ ResultCode.ERROR_DISPLAY_ID_MISMATCH,
+ ResultCode.ERROR_INVALID_DISPLAY_ID,
})
public @interface ResultCode {
/**
@@ -139,13 +142,22 @@
* The client should try to restart input when its {@link android.view.Window} is focused
* again.</p>
*
- * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
+ * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int)
*/
int ERROR_NOT_IME_TARGET_WINDOW = 11;
/**
* Indicates that focused view in the current window is not an editor.
*/
int ERROR_NO_EDITOR = 12;
+ /**
+ * Indicates that there is a mismatch in display ID between IME client and focused Window.
+ */
+ int ERROR_DISPLAY_ID_MISMATCH = 13;
+ /**
+ * Indicates that current IME client is no longer allowed to access to the associated
+ * display.
+ */
+ int ERROR_INVALID_DISPLAY_ID = 14;
}
@ResultCode
@@ -271,6 +283,10 @@
return "ERROR_NULL_EDITOR_INFO";
case ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
return "ERROR_NOT_IME_TARGET_WINDOW";
+ case ResultCode.ERROR_DISPLAY_ID_MISMATCH:
+ return "ERROR_DISPLAY_ID_MISMATCH";
+ case ResultCode.ERROR_INVALID_DISPLAY_ID:
+ return "ERROR_INVALID_DISPLAY_ID";
default:
return "Unknown(" + result + ")";
}
@@ -316,4 +332,15 @@
*/
public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER);
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}.
+ */
+ public static final InputBindResult DISPLAY_ID_MISMATCH =
+ error(ResultCode.ERROR_DISPLAY_ID_MISMATCH);
+
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}.
+ */
+ public static final InputBindResult INVALID_DISPLAY_ID =
+ error(ResultCode.ERROR_INVALID_DISPLAY_ID);
}
diff --git a/core/java/com/android/internal/view/InputMethodClient.java b/core/java/com/android/internal/view/InputMethodClient.java
deleted file mode 100644
index bbd33a2..0000000
--- a/core/java/com/android/internal/view/InputMethodClient.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view;
-
-import android.annotation.IntDef;
-import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-
-import java.lang.annotation.Retention;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-public final class InputMethodClient {
- public static final int START_INPUT_REASON_UNSPECIFIED = 0;
- public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN = 1;
- public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
- public static final int START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API = 3;
- public static final int START_INPUT_REASON_CHECK_FOCUS = 4;
- public static final int START_INPUT_REASON_BOUND_TO_IMMS = 5;
- public static final int START_INPUT_REASON_UNBOUND_FROM_IMMS = 6;
- public static final int START_INPUT_REASON_ACTIVATED_BY_IMMS = 7;
- public static final int START_INPUT_REASON_DEACTIVATED_BY_IMMS = 8;
- public static final int START_INPUT_REASON_SESSION_CREATED_BY_IME = 9;
-
- @Retention(SOURCE)
- @IntDef({START_INPUT_REASON_UNSPECIFIED, START_INPUT_REASON_WINDOW_FOCUS_GAIN,
- START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY,
- START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, START_INPUT_REASON_CHECK_FOCUS,
- START_INPUT_REASON_BOUND_TO_IMMS, START_INPUT_REASON_ACTIVATED_BY_IMMS,
- START_INPUT_REASON_DEACTIVATED_BY_IMMS, START_INPUT_REASON_SESSION_CREATED_BY_IME})
- public @interface StartInputReason {}
-
- public static String getStartInputReason(@StartInputReason final int reason) {
- switch (reason) {
- case START_INPUT_REASON_UNSPECIFIED:
- return "UNSPECIFIED";
- case START_INPUT_REASON_WINDOW_FOCUS_GAIN:
- return "WINDOW_FOCUS_GAIN";
- case START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY:
- return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
- case START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API:
- return "APP_CALLED_RESTART_INPUT_API";
- case START_INPUT_REASON_CHECK_FOCUS:
- return "CHECK_FOCUS";
- case START_INPUT_REASON_BOUND_TO_IMMS:
- return "BOUND_TO_IMMS";
- case START_INPUT_REASON_UNBOUND_FROM_IMMS:
- return "UNBOUND_FROM_IMMS";
- case START_INPUT_REASON_ACTIVATED_BY_IMMS:
- return "ACTIVATED_BY_IMMS";
- case START_INPUT_REASON_DEACTIVATED_BY_IMMS:
- return "DEACTIVATED_BY_IMMS";
- case START_INPUT_REASON_SESSION_CREATED_BY_IME:
- return "SESSION_CREATED_BY_IME";
- default:
- return "Unknown=" + reason;
- }
- }
-
- public static final int UNBIND_REASON_UNSPECIFIED = 0;
- public static final int UNBIND_REASON_SWITCH_CLIENT = 1;
- public static final int UNBIND_REASON_SWITCH_IME = 2;
- public static final int UNBIND_REASON_DISCONNECT_IME = 3;
- public static final int UNBIND_REASON_NO_IME = 4;
- public static final int UNBIND_REASON_SWITCH_IME_FAILED = 5;
- public static final int UNBIND_REASON_SWITCH_USER = 6;
-
- @Retention(SOURCE)
- @IntDef({UNBIND_REASON_UNSPECIFIED, UNBIND_REASON_SWITCH_CLIENT, UNBIND_REASON_SWITCH_IME,
- UNBIND_REASON_DISCONNECT_IME, UNBIND_REASON_NO_IME, UNBIND_REASON_SWITCH_IME_FAILED,
- UNBIND_REASON_SWITCH_USER})
- public @interface UnbindReason {}
-
- public static String getUnbindReason(@UnbindReason final int reason) {
- switch (reason) {
- case UNBIND_REASON_UNSPECIFIED:
- return "UNSPECIFIED";
- case UNBIND_REASON_SWITCH_CLIENT:
- return "SWITCH_CLIENT";
- case UNBIND_REASON_SWITCH_IME:
- return "SWITCH_IME";
- case UNBIND_REASON_DISCONNECT_IME:
- return "DISCONNECT_IME";
- case UNBIND_REASON_NO_IME:
- return "NO_IME";
- case UNBIND_REASON_SWITCH_IME_FAILED:
- return "SWITCH_IME_FAILED";
- case UNBIND_REASON_SWITCH_USER:
- return "SWITCH_USER";
- default:
- return "Unknown=" + reason;
- }
- }
-
- public static String softInputModeToString(@SoftInputModeFlags final int softInputMode) {
- final StringBuilder sb = new StringBuilder();
- final int state = softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE;
- final int adjust = softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST;
- final boolean isForwardNav =
- (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
-
- switch (state) {
- case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
- sb.append("STATE_UNSPECIFIED");
- break;
- case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
- sb.append("STATE_UNCHANGED");
- break;
- case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
- sb.append("STATE_HIDDEN");
- break;
- case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
- sb.append("STATE_ALWAYS_HIDDEN");
- break;
- case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
- sb.append("STATE_VISIBLE");
- break;
- case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
- sb.append("STATE_ALWAYS_VISIBLE");
- break;
- default:
- sb.append("STATE_UNKNOWN(");
- sb.append(state);
- sb.append(")");
- break;
- }
-
- switch (adjust) {
- case LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
- sb.append("|ADJUST_UNSPECIFIED");
- break;
- case LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
- sb.append("|ADJUST_RESIZE");
- break;
- case LayoutParams.SOFT_INPUT_ADJUST_PAN:
- sb.append("|ADJUST_PAN");
- break;
- case LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
- sb.append("|ADJUST_NOTHING");
- break;
- default:
- sb.append("|ADJUST_UNKNOWN(");
- sb.append(adjust);
- sb.append(")");
- break;
- }
-
- if (isForwardNav) {
- // This is a special bit that is set by the system only during the window navigation.
- sb.append("|IS_FORWARD_NAVIGATION");
- }
-
- return sb.toString();
- }
-}
diff --git a/core/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java
index c029bb2..3ea873b 100644
--- a/core/java/com/google/android/collect/Lists.java
+++ b/core/java/com/google/android/collect/Lists.java
@@ -16,6 +16,7 @@
package com.google.android.collect;
+import android.annotation.UnsupportedAppUsage;
import java.util.ArrayList;
import java.util.Collections;
@@ -33,6 +34,7 @@
*
* @return a newly-created, initially-empty {@code ArrayList}
*/
+ @UnsupportedAppUsage
public static <E> ArrayList<E> newArrayList() {
return new ArrayList<E>();
}
diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java
index fc2c9fe..6ba3320 100644
--- a/core/java/com/google/android/collect/Maps.java
+++ b/core/java/com/google/android/collect/Maps.java
@@ -16,6 +16,7 @@
package com.google.android.collect;
+import android.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import java.util.HashMap;
@@ -29,6 +30,7 @@
*
* @return a newly-created, initially-empty {@code HashMap}
*/
+ @UnsupportedAppUsage
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<K, V>();
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c2ca2fc..59c29e2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -45,6 +45,7 @@
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
"android_opengl_EGL14.cpp",
+ "android_opengl_EGL15.cpp",
"android_opengl_EGLExt.cpp",
"android_opengl_GLES10.cpp",
"android_opengl_GLES10Ext.cpp",
@@ -84,8 +85,6 @@
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
- "android_text_LineBreaker.cpp",
- "android_text_MeasuredParagraph.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
"android_os_HidlSupport.cpp",
@@ -161,6 +160,8 @@
"android/graphics/pdf/PdfEditor.cpp",
"android/graphics/pdf/PdfRenderer.cpp",
"android/graphics/pdf/PdfUtils.cpp",
+ "android/graphics/text/LineBreaker.cpp",
+ "android/graphics/text/MeasuredText.cpp",
"android_media_AudioRecord.cpp",
"android_media_AudioSystem.cpp",
"android_media_AudioTrack.cpp",
@@ -239,6 +240,7 @@
shared_libs: [
"libbpf",
+ "libnetdbpf",
"libnetdutils",
"libmemtrack",
"libandroidfw",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6b55ed6..eada690 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,7 @@
extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);
extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env);
extern int register_android_opengl_jni_EGL14(JNIEnv* env);
+extern int register_android_opengl_jni_EGL15(JNIEnv* env);
extern int register_android_opengl_jni_EGLExt(JNIEnv* env);
extern int register_android_opengl_jni_GLES10(JNIEnv* env);
extern int register_android_opengl_jni_GLES10Ext(JNIEnv* env);
@@ -145,6 +146,8 @@
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
+extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_android_view_TextureLayer(JNIEnv* env);
@@ -185,8 +188,6 @@
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
-extern int register_android_text_MeasuredParagraph(JNIEnv* env);
-extern int register_android_text_LineBreaker(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
@@ -1336,8 +1337,6 @@
REG_JNI(register_android_content_res_ApkAssets),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
- REG_JNI(register_android_text_MeasuredParagraph),
- REG_JNI(register_android_text_LineBreaker),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
REG_JNI(register_android_os_Process),
@@ -1369,6 +1368,7 @@
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_EGL14),
+ REG_JNI(register_android_opengl_jni_EGL15),
REG_JNI(register_android_opengl_jni_EGLExt),
REG_JNI(register_android_opengl_jni_GLES10),
REG_JNI(register_android_opengl_jni_GLES10Ext),
@@ -1415,6 +1415,8 @@
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
REG_JNI(register_android_graphics_pdf_PdfRenderer),
+ REG_JNI(register_android_graphics_text_MeasuredText),
+ REG_JNI(register_android_graphics_text_LineBreaker),
REG_JNI(register_android_database_CursorWindow),
REG_JNI(register_android_database_SQLiteConnection),
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 2619107..bb291e7 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,8 +23,6 @@
#include <hwui/Paint.h>
#include <utils/Log.h>
-#include <ResourceCache.h>
-
#include "SkCanvas.h"
#include "SkLatticeIter.h"
#include "SkRegion.h"
@@ -83,12 +81,7 @@
static void finalize(JNIEnv* env, jobject, jlong patchHandle) {
int8_t* patch = reinterpret_cast<int8_t*>(patchHandle);
- if (android::uirenderer::ResourceCache::hasInstance()) {
- Res_png_9patch* p = (Res_png_9patch*) patch;
- android::uirenderer::ResourceCache::getInstance().destructor(p);
- } else {
- delete[] patch;
- }
+ delete[] patch;
}
static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap,
diff --git a/core/jni/android_text_LineBreaker.cpp b/core/jni/android/graphics/text/LineBreaker.cpp
similarity index 96%
rename from core/jni/android_text_LineBreaker.cpp
rename to core/jni/android/graphics/text/LineBreaker.cpp
index 5439107..e1f2f2b 100644
--- a/core/jni/android_text_LineBreaker.cpp
+++ b/core/jni/android/graphics/text/LineBreaker.cpp
@@ -168,8 +168,9 @@
{"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
};
-int register_android_text_LineBreaker(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods));
+int register_android_graphics_text_LineBreaker(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/text/LineBreaker", gMethods,
+ NELEM(gMethods));
}
}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
similarity index 91%
rename from core/jni/android_text_MeasuredParagraph.cpp
rename to core/jni/android/graphics/text/MeasuredText.cpp
index 18f509c..0bfadb4 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "MeasuredParagraph"
+#define LOG_TAG "MeasuredText"
#include "GraphicsJNI.h"
#include "unicode/locid.h"
@@ -83,7 +83,7 @@
}
// Regular JNI
-static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr,
+static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
jcharArray javaText, jboolean computeHyphenation,
jboolean computeLayout) {
ScopedCharArrayRO text(env, javaText);
@@ -147,7 +147,7 @@
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph},
+ {"nBuildMeasuredText", "(J[CZZ)J", (void*) nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
};
@@ -160,10 +160,10 @@
{"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native
};
-int register_android_text_MeasuredParagraph(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph",
+int register_android_graphics_text_MeasuredText(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText",
gMTMethods, NELEM(gMTMethods))
- + RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph$Builder",
+ + RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText$Builder",
gMTBuilderMethods, NELEM(gMTBuilderMethods));
}
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
new file mode 100644
index 0000000..4a30bab
--- /dev/null
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -0,0 +1,557 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/misc.h>
+
+#include <assert.h>
+#include <EGL/egl.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
+{
+ // EGL 1.4 Init
+ jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+ eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal);
+ jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+ eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal);
+ jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+ egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal);
+ jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+ eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
+
+ eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+ eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+ egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+ eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+
+ eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+ eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+ egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+ eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+ jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+ eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+ jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+ eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+ jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
+ eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+
+ jclass eglClass = _env->FindClass("android/opengl/EGL15");
+ jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+ _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+ jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+ _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+ jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+ _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+ // EGL 1.5 init
+ jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+ nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
+
+ jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+ bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
+
+ getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
+ "getBasePointer", "(Ljava/nio/Buffer;)J");
+ getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
+ "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+ getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
+ "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+ positionID = _env->GetFieldID(bufferClass, "position", "I");
+ limitID = _env->GetFieldID(bufferClass, "limit", "I");
+ elementSizeShiftID =
+ _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+ jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+ eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal);
+ jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+ eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
+
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+ eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+ jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+ _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+ jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+ _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *
+getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset)
+{
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+
+ position = _env->GetIntField(buffer, positionID);
+ limit = _env->GetIntField(buffer, limitID);
+ elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+ *remaining = (limit - position) << elementSizeShift;
+ pointer = _env->CallStaticLongMethod(nioAccessClass,
+ getBasePointerID, buffer);
+ if (pointer != 0L) {
+ *array = NULL;
+ return reinterpret_cast<void*>(pointer);
+ }
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
+ getBaseArrayID, buffer);
+ *offset = _env->CallStaticIntMethod(nioAccessClass,
+ getBaseArrayOffsetID, buffer);
+
+ return NULL;
+}
+
+static void
+releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
+{
+ _env->ReleasePrimitiveArrayCritical(array, data,
+ commit ? 0 : JNI_ABORT);
+}
+
+static void *
+fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+ if (obj == NULL){
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Object is set to null.");
+ }
+
+ jlong handle = _env->CallLongMethod(obj, mid);
+ return reinterpret_cast<void*>(handle);
+}
+
+static jobject
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
+ if (cls == eglimageClass &&
+ (EGLImage)handle == EGL_NO_IMAGE) {
+ return eglNoImageObject;
+ }
+
+ return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
+/* EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jint type, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLSync _returnValue = (EGLSync) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ _returnValue = eglCreateSync(
+ (EGLDisplay)dpy_native,
+ (EGLenum)type,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, eglsyncClass, eglsyncConstructor, _returnValue);
+}
+
+/* EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) */
+static jboolean
+android_eglDestroySync
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync) {
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+ _returnValue = eglDestroySync(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native
+ );
+ return (jboolean)_returnValue;
+}
+
+/* EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) */
+static jint
+android_eglClientWaitSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags, jlong timeout) {
+ EGLint _returnValue = (EGLint) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+ _returnValue = eglClientWaitSync(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)flags,
+ (EGLTime)timeout
+ );
+ return (jint)_returnValue;
+}
+
+/* EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) */
+static jboolean
+android_eglGetSyncAttrib
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint attribute, jlongArray value_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+ EGLAttrib *value_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *value = (EGLAttrib *) 0;
+
+ if (!value_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "value == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(value_ref) - offset;
+ value_base = (EGLAttrib *)
+ _env->GetLongArrayElements(value_ref, (jboolean *)0);
+ value = value_base + offset;
+
+ _returnValue = eglGetSyncAttrib(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)attribute,
+ (EGLAttrib *)value
+ );
+
+exit:
+ if (value_base) {
+ _env->ReleaseLongArrayElements(value_ref, (jlong*)value_base,
+ _exception ? JNI_ABORT: 0);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return (jboolean)_returnValue;
+}
+
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+ (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLDisplay _returnValue = (EGLDisplay) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ _returnValue = eglGetPlatformDisplay(
+ (EGLenum)platform,
+ (void *)native_display,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformWindowSurface
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_window_buf, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ jarray _array = (jarray) 0;
+ jint _bufferOffset = (jint) 0;
+ EGLSurface _returnValue = (EGLSurface) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+ jint _native_windowRemaining;
+ void *native_window = (void *) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _attrib_listRemaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!native_window_buf) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "native_window == null";
+ goto exit;
+ }
+ native_window = (void *)getPointer(_env, native_window_buf, (jarray*)&_array, &_native_windowRemaining, &_bufferOffset);
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ if (native_window == NULL) {
+ char * _native_windowBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+ native_window = (void *) (_native_windowBase + _bufferOffset);
+ }
+ _returnValue = eglCreatePlatformWindowSurface(
+ (EGLDisplay)dpy_native,
+ (EGLConfig)config_native,
+ (void *)native_window,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_array) {
+ releasePointer(_env, _array, native_window, _exception ? JNI_FALSE : JNI_TRUE);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformPixmapSurface
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ jarray _array = (jarray) 0;
+ jint _bufferOffset = (jint) 0;
+ EGLSurface _returnValue = (EGLSurface) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+ jint _native_pixmapRemaining;
+ void *native_pixmap = (void *) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _attrib_listRemaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!native_pixmap_buf) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "native_pixmap == null";
+ goto exit;
+ }
+ native_pixmap = (void *)getPointer(_env, native_pixmap_buf, (jarray*)&_array, &_native_pixmapRemaining, &_bufferOffset);
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ if (native_pixmap == NULL) {
+ char * _native_pixmapBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+ native_pixmap = (void *) (_native_pixmapBase + _bufferOffset);
+ }
+ _returnValue = eglCreatePlatformPixmapSurface(
+ (EGLDisplay)dpy_native,
+ (EGLConfig)config_native,
+ (void *)native_pixmap,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_array) {
+ releasePointer(_env, _array, native_pixmap, _exception ? JNI_FALSE : JNI_TRUE);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */
+static jboolean
+android_eglWaitSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags) {
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+ _returnValue = eglWaitSync(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)flags
+ );
+ return (jboolean)_returnValue;
+}
+
+static const char *classPathName = "android/opengl/EGL15";
+
+static const JNINativeMethod methods[] = {
+{"_nativeClassInit", "()V", (void*)nativeClassInit },
+{"eglCreateSync", "(Landroid/opengl/EGLDisplay;I[JI)Landroid/opengl/EGLSync;", (void *) android_eglCreateSync },
+{"eglDestroySync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)Z", (void *) android_eglDestroySync },
+{"eglClientWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;IJ)I", (void *) android_eglClientWaitSync },
+{"eglGetSyncAttrib", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I[JI)Z", (void *) android_eglGetSyncAttrib },
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
+{"eglCreatePlatformWindowSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformWindowSurface },
+{"eglCreatePlatformPixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformPixmapSurface },
+{"eglWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I)Z", (void *) android_eglWaitSync },
+};
+
+int register_android_opengl_jni_EGL15(JNIEnv *_env)
+{
+ int err;
+ err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));
+ return err;
+}
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b70485d..e64da5c 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -23,17 +23,25 @@
namespace {
+int getCanLoadSystemLibraries_native() {
+ return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries();
+}
+
void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
ScopedUtfChars pathChars(env, path);
android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn,
+ jobject rulesFd, jlong rulesOffset, jlong rulesLength) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars appNameChars(env, appName);
ScopedUtfChars appPrefChars(env, appPref);
+
+ int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
+
android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
- appPrefChars.c_str(), devOptIn);
+ appPrefChars.c_str(), devOptIn, rulesFd_native, rulesOffset, rulesLength);
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -51,8 +59,9 @@
}
const JNINativeMethod g_methods[] = {
+ { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
{ "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
- { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) },
+ { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
{ "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
{ "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
};
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index ecad6c0..d023d22 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -23,6 +23,7 @@
#include <atomic>
#include <fcntl.h>
#include <inttypes.h>
+#include <mutex>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -69,6 +70,7 @@
// Class state.
jclass mClass;
jmethodID mExecTransact;
+ jmethodID mGetInterfaceDescriptor;
// Object state.
jfieldID mObject;
@@ -326,8 +328,32 @@
env->DeleteGlobalRef(mObject);
}
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
+ const String16& getInterfaceDescriptor() const override
+ {
+ call_once(mPopulateDescriptor, [this] {
+ JNIEnv* env = javavm_to_jnienv(mVM);
+
+ ALOGV("getInterfaceDescriptor() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
+
+ jstring descriptor = (jstring)env->CallObjectMethod(mObject, gBinderOffsets.mGetInterfaceDescriptor);
+
+ if (descriptor == nullptr) {
+ return;
+ }
+
+ static_assert(sizeof(jchar) == sizeof(char16_t), "");
+ const jchar* descriptorChars = env->GetStringChars(descriptor, nullptr);
+ const char16_t* rawDescriptor = reinterpret_cast<const char16_t*>(descriptorChars);
+ jsize rawDescriptorLen = env->GetStringLength(descriptor);
+ mDescriptor = String16(rawDescriptor, rawDescriptorLen);
+ env->ReleaseStringChars(descriptor, descriptorChars);
+ });
+
+ return mDescriptor;
+ }
+
+ status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
{
JNIEnv* env = javavm_to_jnienv(mVM);
@@ -376,7 +402,7 @@
return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
}
- virtual status_t dump(int fd, const Vector<String16>& args)
+ status_t dump(int fd, const Vector<String16>& args) override
{
return 0;
}
@@ -384,6 +410,9 @@
private:
JavaVM* const mVM;
jobject const mObject; // GlobalRef to Java Binder
+
+ mutable std::once_flag mPopulateDescriptor;
+ mutable String16 mDescriptor;
};
// ----------------------------------------------------------------------------
@@ -926,6 +955,8 @@
gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
+ gBinderOffsets.mGetInterfaceDescriptor = GetMethodIDOrDie(env, clazz, "getInterfaceDescriptor",
+ "()Ljava/lang/String;");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
return RegisterMethodsOrDie(
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 109e65c..b3ff4db 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -32,8 +32,8 @@
#include <utils/misc.h>
#include "android-base/unique_fd.h"
-#include "bpf/BpfNetworkStats.h"
#include "bpf/BpfUtils.h"
+#include "netdbpf/BpfNetworkStats.h"
using android::bpf::hasBpfSupport;
using android::bpf::parseBpfNetworkStatsDetail;
diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index a41edf3..1f63be9 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -512,7 +512,9 @@
optional int32 vts_coverage = 43;
optional string zygote = 44;
- // Next Tag: 45
+ optional string gfx_driver_whitelist_0 = 45;
+
+ // Next Tag: 46
}
optional Ro ro = 21;
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a02602e..47dbc07 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -397,9 +397,10 @@
// Ordered GPU debug layer list
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-
// App will load ANGLE instead of native GLES drivers.
optional SettingProto angle_enabled_app = 3;
+ // App that can provide layer libraries.
+ optional SettingProto debug_layer_app = 4;
}
optional Gpu gpu = 59;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d24b002..1ae5f03 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2770,7 +2770,7 @@
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to use
- {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
+ {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
to hide non-system-overlay windows.
<p>Not for use by third-party applications.
@hide
diff --git a/core/res/res/layout/notification_material_media_action.xml b/core/res/res/layout/notification_material_media_action.xml
index 900ca2d..dd79a0b 100644
--- a/core/res/res/layout/notification_material_media_action.xml
+++ b/core/res/res/layout/notification_material_media_action.xml
@@ -18,7 +18,6 @@
<ImageButton
xmlns:android="http://schemas.android.com/apk/res/android"
style="@android:style/Widget.Material.Button.Borderless.Small"
- android:id="@+id/action0"
android:layout_width="@dimen/media_notification_action_button_size"
android:layout_height="@dimen/media_notification_action_button_size"
android:paddingBottom="8dp"
@@ -28,4 +27,5 @@
android:layout_marginEnd="2dp"
android:gravity="center"
android:background="@drawable/notification_material_media_action_background"
+ android:visibility="gone"
/>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index b4e26483..5cb93eb 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -59,7 +59,26 @@
android:orientation="horizontal"
android:layoutDirection="ltr"
style="@style/NotificationMediaActionContainer" >
- <!-- media buttons will be added here -->
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action0"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action1"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action2"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action3"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action4"
+ />
</LinearLayout>
</LinearLayout>
</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 3a0912b..01b0866 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -65,7 +65,18 @@
android:layoutDirection="ltr"
android:orientation="horizontal"
>
- <!-- media buttons will be added here -->
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action0"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action1"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action2"
+ />
</LinearLayout>
</LinearLayout>
</FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cdb65ed..32cf2e8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4551,6 +4551,11 @@
<attr name="typeface" />
<!-- Font family (named by string or as a font resource reference) for the text. -->
<attr name="fontFamily" />
+ <!-- Specifies the {@link android.os.LocaleList} for the text.
+ May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
+ When not specified or an empty string is given, it will fallback to the default one.
+ {@see android.os.LocaleList#forLanguageTags(String)} -->
+ <attr name="textLocale" format="string" />
<!-- Color of the text selection highlight. -->
<attr name="textColorHighlight" />
<!-- Color of the hint text. -->
@@ -4642,6 +4647,13 @@
<attr name="textFontWeight" />
<!-- Font family (named by string or as a font resource reference) for the text. -->
<attr name="fontFamily" />
+ <!-- Specifies the {@link android.os.LocaleList} for the text in this TextView.
+ If not given, the system default will be used.
+ May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
+ When not specified or an empty string is given, it will fallback to the default one.
+ {@see android.os.LocaleList#forLanguageTags(String)}
+ {@see android.text.TextView#setTextLocales(android.os.LocaleList)} -->
+ <attr name="textLocale" format="string" />
<!-- Text color for links. -->
<attr name="textColorLink" />
<!-- Makes the cursor visible (the default) or invisible. -->
@@ -7882,6 +7894,9 @@
<!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
<attr name="supportsAmbientMode" format="boolean" />
+ <!-- Uri that specifies a settings Slice for this wallpaper. -->
+ <attr name="settingsSliceUri" />
+
</declare-styleable>
<!-- Use <code>dream</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aeeba59..e257a5c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -378,8 +378,8 @@
Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
[IP config] Optional. If empty or not specified - DHCP will be used, otherwise
use the following format to specify static IP configuration:
- ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
- domains=<comma-sep-domains>
+ ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
+ domains=<comma-sep-domains>
-->
<string-array translatable="false" name="config_ethernet_interfaces">
<!--
@@ -697,6 +697,10 @@
<!-- Wifi driver supports IEEE80211AC for softap -->
<bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
+ <!-- Indicates that local-only hotspot should be brought up at 5GHz. This option is
+ for automotive builds only (the one that have PackageManager#FEATURE_AUTOMOTIVE) -->
+ <bool translatable="false" name="config_wifi_local_only_hotspot_5ghz">false</bool>
+
<!-- Flag indicating whether we should enable the automatic brightness.
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
@@ -3367,22 +3371,22 @@
<bool name="config_sendPackageName">false</bool>
<!-- Name for the set of keys associating package names -->
- <string name="config_help_package_name_key" translatable="false"></string>
+ <string name="config_helpPackageNameKey" translatable="false"></string>
<!-- Name for the set of values of package names -->
- <string name="config_help_package_name_value" translatable="false"></string>
+ <string name="config_helpPackageNameValue" translatable="false"></string>
<!-- Intent key for the package name keys -->
- <string name="config_help_intent_extra_key" translatable="false"></string>
+ <string name="config_helpIntentExtraKey" translatable="false"></string>
<!-- Intent key for package name values -->
- <string name="config_help_intent_name_key" translatable="false"></string>
+ <string name="config_helpIntentNameKey" translatable="false"></string>
<!-- Intent key for the package name keys -->
- <string name="config_feedback_intent_extra_key" translatable="false"></string>
+ <string name="config_feedbackIntentExtraKey" translatable="false"></string>
<!-- Intent key for package name values -->
- <string name="config_feedback_intent_name_key" translatable="false"></string>
+ <string name="config_feedbackIntentNameKey" translatable="false"></string>
<!-- The apps that need to be hidden when they are disabled -->
<string-array name="config_hideWhenDisabled_packageNames"></string-array>
@@ -3487,7 +3491,7 @@
<string name="config_headlineFontFamilyMedium">@string/font_family_button_material</string>
<!-- Size of icon shown beside a preference locked by admin -->
- <dimen name="config_restricted_icon_size">@dimen/restricted_icon_size_material</dimen>
+ <dimen name="config_restrictedIconSize">@dimen/restricted_icon_size_material</dimen>
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
@@ -3553,4 +3557,8 @@
<!-- Whether or not the "SMS app service" feature is enabled -->
<bool name="config_useSmsAppService">true</bool>
+
+ <!-- Component name for default assistant on this device -->
+ <string name="config_defaultAssistantComponentName">#+UNSET</string>
+
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2e42e4a..fd688a7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2915,6 +2915,8 @@
<public name="minimumUiTimeout" />
<public name="isLightTheme" />
<public name="isSplitRequired" />
+ <public name="textLocale" />
+ <public name="settingsSliceUri" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
@@ -2930,17 +2932,17 @@
<public-group type="string" first-id="0x0104001b">
<!-- @hide @SystemApi -->
- <public name="config_help_package_name_key" />
+ <public name="config_helpPackageNameKey" />
<!-- @hide @SystemApi -->
- <public name="config_help_package_name_value" />
+ <public name="config_helpPackageNameValue" />
<!-- @hide @SystemApi -->
- <public name="config_help_intent_extra_key" />
+ <public name="config_helpIntentExtraKey" />
<!-- @hide @SystemApi -->
- <public name="config_help_intent_name_key" />
+ <public name="config_helpIntentNameKey" />
<!-- @hide @SystemApi -->
- <public name="config_feedback_intent_extra_key" />
+ <public name="config_feedbackIntentExtraKey" />
<!-- @hide @SystemApi -->
- <public name="config_feedback_intent_name_key" />
+ <public name="config_feedbackIntentNameKey" />
</public-group>
<public-group type="bool" first-id="0x01110000">
@@ -2950,7 +2952,7 @@
<public-group type="dimen" first-id="0x01050007">
<!-- @hide @SystemApi -->
- <public name="config_restricted_icon_size" />
+ <public name="config_restrictedIconSize" />
</public-group>
<!-- ===============================================================
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fa44061..9ea82a9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2924,55 +2924,55 @@
<!-- Title for EditText context menu [CHAR LIMIT=20] -->
<string name="editTextMenuTitle">Text actions</string>
- <!-- Label for item in the text selection menu to trigger an Email app. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger an Email app. Should be a verb. [CHAR LIMIT=30] -->
<string name="email">Email</string>
<!-- Accessibility description for an item in the text selection menu to trigger an Email app [CHAR LIMIT=NONE] -->
<string name="email_desc">Email selected address</string>
- <!-- Label for item in the text selection menu to trigger a Dialer app. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger a Dialer app. Should be a verb. [CHAR LIMIT=30] -->
<string name="dial">Call</string>
<!-- Accessibility description for an item in the text selection menu to call a phone number [CHAR LIMIT=NONE] -->
<string name="dial_desc">Call selected phone number</string>
- <!-- Label for item in the text selection menu to trigger a Map app. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger a Map app. Should be a verb. [CHAR LIMIT=30] -->
<string name="map">Map</string>
<!-- Accessibility description for an item in the text selection menu to open maps for an address [CHAR LIMIT=NONE] -->
<string name="map_desc">Locate selected address</string>
- <!-- Label for item in the text selection menu to trigger a Browser app. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger a Browser app. Should be a verb. [CHAR LIMIT=30] -->
<string name="browse">Open</string>
<!-- Accessibility description for an item in the text selection menu to open a URL in a browser [CHAR LIMIT=NONE] -->
<string name="browse_desc">Open selected URL</string>
- <!-- Label for item in the text selection menu to trigger an SMS app. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger an SMS app. Should be a verb. [CHAR LIMIT=30] -->
<string name="sms">Message</string>
<!-- Accessibility description for an item in the text selection menu to send an SMS to a phone number [CHAR LIMIT=NONE] -->
<string name="sms_desc">Message selected phone number</string>
- <!-- Label for item in the text selection menu to trigger adding a contact. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger adding a contact. Should be a verb. [CHAR LIMIT=30] -->
<string name="add_contact">Add</string>
<!-- Accessibility description for an item in the text selection menu to add the selected detail to contacts [CHAR LIMIT=NONE] -->
<string name="add_contact_desc">Add to contacts</string>
- <!-- Label for item in the text selection menu to view the calendar for the selected time/date. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to view the calendar for the selected time/date. Should be a verb. [CHAR LIMIT=30] -->
<string name="view_calendar">View</string>
<!-- Accessibility description for an item in the text selection menu to view the calendar for a date [CHAR LIMIT=NONE]-->
<string name="view_calendar_desc">View selected time in calendar</string>
- <!-- Label for item in the text selection menu to create a calendar event at the selected time/date. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to create a calendar event at the selected time/date. Should be a verb. [CHAR LIMIT=30] -->
<string name="add_calendar_event">Schedule</string>
<!-- Accessibility description for an item in the text selection menu to schedule an event for a date [CHAR LIMIT=NONE] -->
<string name="add_calendar_event_desc">Schedule event for selected time</string>
- <!-- Label for item in the text selection menu to track a selected flight number. Should be a verb. [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to track a selected flight number. Should be a verb. [CHAR LIMIT=30] -->
<string name="view_flight">Track</string>
<!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6f28b2c..81a1bf8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -186,6 +186,8 @@
<java-symbol type="id" name="action0" />
<java-symbol type="id" name="action1" />
<java-symbol type="id" name="action2" />
+ <java-symbol type="id" name="action3" />
+ <java-symbol type="id" name="action4" />
<java-symbol type="id" name="big_picture" />
<java-symbol type="id" name="big_text" />
<java-symbol type="id" name="chronometer" />
@@ -1873,6 +1875,7 @@
<java-symbol type="bool" name="config_wifi_background_scan_support" />
<java-symbol type="bool" name="config_wifi_dual_band_support" />
<java-symbol type="bool" name="config_wifi_convert_apband_5ghz_to_any" />
+ <java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" />
<java-symbol type="bool" name="config_wifi_fast_bss_transition_enabled" />
<java-symbol type="bool" name="config_wimaxEnabled" />
<java-symbol type="bool" name="show_ongoing_ime_switcher" />
@@ -3270,12 +3273,12 @@
<java-symbol type="integer" name="default_data_warning_level_mb" />
<java-symbol type="bool" name="config_useVideoPauseWorkaround" />
<java-symbol type="bool" name="config_sendPackageName" />
- <java-symbol type="string" name="config_help_package_name_key" />
- <java-symbol type="string" name="config_help_package_name_value" />
- <java-symbol type="string" name="config_help_intent_extra_key" />
- <java-symbol type="string" name="config_help_intent_name_key" />
- <java-symbol type="string" name="config_feedback_intent_extra_key" />
- <java-symbol type="string" name="config_feedback_intent_name_key" />
+ <java-symbol type="string" name="config_helpPackageNameKey" />
+ <java-symbol type="string" name="config_helpPackageNameValue" />
+ <java-symbol type="string" name="config_helpIntentExtraKey" />
+ <java-symbol type="string" name="config_helpIntentNameKey" />
+ <java-symbol type="string" name="config_feedbackIntentExtraKey" />
+ <java-symbol type="string" name="config_feedbackIntentNameKey" />
<java-symbol type="array" name="config_hideWhenDisabled_packageNames" />
@@ -3478,4 +3481,6 @@
<java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
<java-symbol type="bool" name="config_useSmsAppService" />
+
+ <java-symbol type="string" name="config_defaultAssistantComponentName" />
</resources>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 307e2e8..041fb7e 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -47,7 +47,6 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
- conscrypt \
telephony-common \
org.apache.http.legacy \
android.test.base \
diff --git a/core/tests/coretests/assets/fonts/1em_bidi_font.ttf b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf
new file mode 100644
index 0000000..4599254
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf
Binary files differ
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 6256d08..0036186 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -13,31 +13,149 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.content;
-import android.content.ContentResolver;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
-@Suppress // Failing.
-public class ContentResolverTest extends AndroidTestCase {
- private ContentResolver mContentResolver;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ImageDecoder;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Size;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContentResolver = mContext.getContentResolver();
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentResolverTest {
+
+ private ContentResolver mResolver;
+ private IContentProvider mProvider;
+ private ContentProviderClient mClient;
+
+ private int mSize = 256_000;
+ private MemoryFile mImage;
+
+ @Before
+ public void setUp() throws Exception {
+ mResolver = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ mProvider = mock(IContentProvider.class);
+ mClient = new ContentProviderClient(mResolver, mProvider, false);
+
+ mImage = new MemoryFile("temp.png", mSize);
}
- @LargeTest
- public void testCursorFinalizer() throws Exception {
- // TODO: Want a test case that more predictably reproduce this issue. Selected
- // 600 as this causes the problem 100% of the runs on current hw, it might not
- // do so on some other configuration though.
- for (int i = 0; i < 600; i++) {
- mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
- }
+ @After
+ public void tearDown() throws Exception {
+ mImage.close();
+ mImage = null;
+ }
+
+ private void initImage(int width, int height) throws Exception {
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+
+ canvas.drawColor(Color.RED);
+
+ final Paint paint = new Paint();
+ paint.setColor(Color.BLUE);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(0, 0, width / 2, height / 2, paint);
+
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, mImage.getOutputStream());
+
+ final AssetFileDescriptor afd = new AssetFileDescriptor(
+ new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
+ when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd);
+ }
+
+ private static void assertImageAspectAndContents(Bitmap bitmap) {
+ // And correct aspect ratio
+ final int before = (100 * 1280) / 960;
+ final int after = (100 * bitmap.getWidth()) / bitmap.getHeight();
+ assertEquals(before, after);
+
+ // And scaled correctly
+ final int halfX = bitmap.getWidth() / 2;
+ final int halfY = bitmap.getHeight() / 2;
+ assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10));
+ assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10));
+ }
+
+ @Test
+ public void testLoadThumbnail_Normal() throws Exception {
+ initImage(1280, 960);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(1280, 960), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be untouched
+ assertEquals(1280, res.getWidth());
+ assertEquals(960, res.getHeight());
+
+ assertImageAspectAndContents(res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Scaling() throws Exception {
+ initImage(1280, 960);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(320, 240), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be much smaller
+ assertTrue(res.getWidth() <= 640);
+ assertTrue(res.getHeight() <= 480);
+
+ assertImageAspectAndContents(res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Aspect() throws Exception {
+ initImage(1280, 960);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(240, 320), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be much smaller
+ assertTrue(res.getWidth() <= 640);
+ assertTrue(res.getHeight() <= 480);
+
+ assertImageAspectAndContents(res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Tiny() throws Exception {
+ initImage(32, 24);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(320, 240), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be untouched
+ assertEquals(32, res.getWidth());
+ assertEquals(24, res.getHeight());
+
+ assertImageAspectAndContents(res);
}
}
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
new file mode 100644
index 0000000..c8a3098
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityThread;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ContextTest {
+ @Test
+ public void testDisplayIdForSystemContext() {
+ final Context systemContext =
+ ActivityThread.currentActivityThread().getSystemContext();
+
+ assertEquals(systemContext.getDisplay().getDisplayId(), systemContext.getDisplayId());
+ }
+
+ @Test
+ public void testDisplayIdForTestContext() {
+ final Context testContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ assertEquals(testContext.getDisplay().getDisplayId(), testContext.getDisplayId());
+ }
+
+ @Test
+ public void testDisplayIdForDefaultDisplayContext() {
+ final Context testContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final WindowManager wm = testContext.getSystemService(WindowManager.class);
+ final Context defaultDisplayContext =
+ testContext.createDisplayContext(wm.getDefaultDisplay());
+
+ assertEquals(defaultDisplayContext.getDisplay().getDisplayId(),
+ defaultDisplayContext.getDisplayId());
+ }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 80281b6..6966448 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -41,6 +41,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.Context;
import android.os.FileUtils.MemoryPipe;
@@ -510,6 +511,20 @@
MODE_WRITE_ONLY | MODE_CREATE | MODE_APPEND);
}
+ @Test
+ public void testTranslateMode_Invalid() throws Exception {
+ try {
+ translateModeStringToPosix("rwx");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ translateModeStringToPosix("");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
private static void assertTranslate(String string, int posix, int pfd) {
assertEquals(posix, translateModeStringToPosix(string));
assertEquals(string, translateModePosixToString(posix));
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 60abd94..9778acb 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -368,6 +368,8 @@
Settings.Global.PRIV_APP_OOB_ENABLED,
Settings.Global.PRIV_APP_OOB_LIST,
Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
Settings.Global.RADIO_BLUETOOTH,
Settings.Global.RADIO_CELL,
@@ -450,6 +452,7 @@
Settings.Global.GPU_DEBUG_APP,
Settings.Global.GPU_DEBUG_LAYERS,
Settings.Global.ANGLE_ENABLED_APP,
+ Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
new file mode 100644
index 0000000..1208d7c
--- /dev/null
+++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.text;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.content.Context;
+import android.graphics.Path;
+import android.graphics.Typeface;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.method.MetaKeyKeyListener;
+import android.view.KeyEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LayoutBidiCursorPathTest {
+
+ private static final float BIDI_TEXT_SIZE = 12f;
+ private static final String LTR_TEXT = "hello";
+ private static final String RTL_TEXT = "Ù…Ø±ØØ¨Ø§";
+
+ private SpannableStringBuilder mBidiText;
+ private TextPaint mTextPaint;
+
+ @Before
+ public void setup() {
+ mBidiText = new SpannableStringBuilder(LTR_TEXT + RTL_TEXT);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ mTextPaint = new TextPaint();
+ mTextPaint.setTypeface(
+ Typeface.createFromAsset(context.getAssets(), "fonts/1em_bidi_font.ttf"));
+ mTextPaint.setTextSize(BIDI_TEXT_SIZE);
+ }
+
+ @Test
+ public void testGetCursorPathSegments() {
+ // Setup layout and Act.
+ final Path actualPath = new Path();
+ setupLayoutAndGetCursorPath(actualPath);
+
+ // Expected path.
+ final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f;
+ final int top = 0;
+ // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here.
+ final int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f);
+
+ final Path expectedPath = new Path();
+
+ expectedPath.moveTo(h1, top);
+ expectedPath.lineTo(h1, bottom);
+
+ // Assert.
+ assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f);
+ }
+
+ @Test
+ public void testGetCursorPath_whenShiftIsPressed() {
+ // When shift is pressed a triangle is drawn at the bottom quarter of the cursor.
+ // Set up key.
+ final MetaKeyKeyListener metaKeyKeyListener = new MetaKeyKeyListener() {};
+ metaKeyKeyListener
+ .onKeyDown(null /*view*/, mBidiText, KeyEvent.KEYCODE_SHIFT_RIGHT, null /*keyEvent*/);
+
+ // Setup layout and Act.
+ final Path actualPath = new Path();
+ setupLayoutAndGetCursorPath(actualPath);
+
+ // Expected path.
+ final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f;
+ final int top = 0;
+ // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here.
+ int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f);
+ // Draw a triangle at the bottom quarter of the cursor, thus cut the cursor to its 3/4
+ // length.
+ final int dist = (bottom - top) / 4;
+ bottom -= dist;
+
+ final Path expectedPath = new Path();
+
+ expectedPath.moveTo(h1, top);
+ expectedPath.lineTo(h1, bottom);
+
+ expectedPath.moveTo(h1, bottom);
+ expectedPath.lineTo(h1 - dist, bottom + dist);
+
+ expectedPath.moveTo(h1 - dist, bottom + dist - 0.5f);
+ expectedPath.lineTo(h1 + dist, bottom + dist - 0.5f);
+
+ expectedPath.moveTo(h1 + dist, bottom + dist);
+ expectedPath.lineTo(h1, bottom);
+
+ // Assert.
+ assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f);
+ }
+
+ @Test
+ public void testGetCursorPath_whenAltIsPressed() {
+ // When alt is pressed a triangle is drawn at the top quarter of the cursor.
+ // Set up key.
+ final MetaKeyKeyListener metaKeyKeyListener = new MetaKeyKeyListener() {};
+ metaKeyKeyListener
+ .onKeyDown(null /*view*/, mBidiText, KeyEvent.KEYCODE_ALT_RIGHT, null /*keyEvent*/);
+
+ // Setup layout and Act.
+ final Path actualPath = new Path();
+ setupLayoutAndGetCursorPath(actualPath);
+
+ // Expected path.
+ final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f;
+ int top = 0;
+ // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here.
+ final int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f);
+ // Draw a triangle at the top quarter of the cursor, thus cut the cursor to its 3/4 length.
+ final int dist = (bottom - top) / 4;
+ top += dist;
+
+ final Path expectedPath = new Path();
+
+ expectedPath.moveTo(h1, top);
+ expectedPath.lineTo(h1, bottom);
+
+ expectedPath.moveTo(h1, top);
+ expectedPath.lineTo(h1 - dist, top - dist);
+
+ expectedPath.moveTo(h1 - dist, top - dist + 0.5f);
+ expectedPath.lineTo(h1 + dist, top - dist + 0.5f);
+
+ expectedPath.moveTo(h1 + dist, top - dist);
+ expectedPath.lineTo(h1, top);
+
+ // Assert.
+ assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f);
+ }
+
+ private void setupLayoutAndGetCursorPath(Path path) {
+ final Layout layout = StaticLayout.Builder.obtain(
+ mBidiText, 0, mBidiText.length(), mTextPaint, Integer.MAX_VALUE)
+ .setIncludePad(false)
+ .build();
+
+ layout.getCursorPath(LTR_TEXT.length(), path, mBidiText);
+ }
+}
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index f3d6013..3d15eb9 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -73,7 +73,7 @@
assertEquals(0, mt.getWidths().size());
assertEquals(0, mt.getSpanEndCache().size());
assertEquals(0, mt.getFontMetrics().size());
- assertNull(mt.getNativeMeasuredParagraph());
+ assertNull(mt.getMeasuredText());
// Recycle it
MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
@@ -85,7 +85,7 @@
assertEquals(0, mt2.getWidths().size());
assertEquals(0, mt2.getSpanEndCache().size());
assertEquals(0, mt2.getFontMetrics().size());
- assertNull(mt.getNativeMeasuredParagraph());
+ assertNull(mt.getMeasuredText());
mt2.recycle();
}
@@ -107,7 +107,7 @@
assertEquals(10, mt.getWidths().get(2), 0);
assertEquals(0, mt.getSpanEndCache().size());
assertEquals(0, mt.getFontMetrics().size());
- assertNull(mt.getNativeMeasuredParagraph());
+ assertNull(mt.getMeasuredText());
// Recycle it
MeasuredParagraph mt2 =
@@ -124,7 +124,7 @@
assertEquals(5, mt2.getWidths().get(2), 0);
assertEquals(0, mt2.getSpanEndCache().size());
assertEquals(0, mt2.getFontMetrics().size());
- assertNull(mt.getNativeMeasuredParagraph());
+ assertNull(mt.getMeasuredText());
mt2.recycle();
}
@@ -144,7 +144,7 @@
assertEquals(1, mt.getSpanEndCache().size());
assertEquals(3, mt.getSpanEndCache().get(0));
assertNotEquals(0, mt.getFontMetrics().size());
- assertNotNull(mt.getNativeMeasuredParagraph());
+ assertNotNull(mt.getMeasuredText());
// Recycle it
MeasuredParagraph mt2 =
@@ -159,7 +159,7 @@
assertEquals(1, mt2.getSpanEndCache().size());
assertEquals(4, mt2.getSpanEndCache().get(0));
assertNotEquals(0, mt2.getFontMetrics().size());
- assertNotNull(mt.getNativeMeasuredParagraph());
+ assertNotNull(mt.getMeasuredText());
mt2.recycle();
}
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index c98e646..7360e9f 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -20,7 +20,6 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.Suppress;
import android.text.InputType;
import android.util.KeyUtils;
import android.view.KeyEvent;
@@ -239,7 +238,6 @@
}
@Test
- @Suppress
public void testEmojiModifier() {
EditorState state = new EditorState();
@@ -256,20 +254,15 @@
// Isolated multiple emoji modifier
state.setByString("| U+1F3FB U+1F3FB");
forwardDelete(state, 0);
- state.assertEquals("| U+1F3FB");
- forwardDelete(state, 0);
state.assertEquals("|");
// Multiple emoji modifiers
state.setByString("| U+1F466 U+1F3FB U+1F3FB");
forwardDelete(state, 0);
- state.assertEquals("| U+1F3FB");
- forwardDelete(state, 0);
state.assertEquals("|");
}
@Test
- @Suppress
public void testMixedEdgeCases() {
EditorState state = new EditorState();
@@ -318,7 +311,7 @@
// COMBINING ENCLOSING KEYCAP + emoji modifier
state.setByString("| '1' U+20E3 U+1F3FB");
forwardDelete(state, 0);
- state.assertEquals("| U+1F3FB");
+ state.assertEquals("|");
// Emoji modifier + COMBINING ENCLOSING KEYCAP
state.setByString("| U+1F466 U+1F3FB U+20E3");
@@ -360,7 +353,7 @@
// Variation selector + emoji modifier
state.setByString("| U+2665 U+FE0F U+1F3FB");
forwardDelete(state, 0);
- state.assertEquals("| U+1F3FB");
+ state.assertEquals("|");
// Emoji modifier + variation selector
state.setByString("| U+1F466 U+1F3FB U+FE0F");
@@ -396,7 +389,7 @@
// Start with ZERO WIDTH JOINER + emoji modifier
state.setByString("| U+200D U+1F3FB");
forwardDelete(state, 0);
- state.assertEquals("| U+1F3FB");
+ state.assertEquals("|");
// ZERO WIDTH JOINER + emoji modifier
state.setByString("| U+1F469 U+200D U+1F3FB");
@@ -418,8 +411,6 @@
// Regional indicator symbol + emoji modifier
state.setByString("| U+1F1FA U+1F3FB");
forwardDelete(state, 0);
- state.assertEquals("| U+1F3FB");
- forwardDelete(state, 0);
state.assertEquals("|");
// Emoji modifier + regional indicator symbol
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 1a480c7..023526f 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -54,6 +54,13 @@
MotionEvent motionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN,
pointerCount, properties, coords,
0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0);
+
+ MotionEvent motionEvent_Single = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN /* action */, 0f /* x */, 0f /* y */, 0/* pressure */, 0 /* size */,
+ 0 /* metaState */, 0 /* xPrecision */, 0 /* yPrecision */,
+ 0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_TOUCHSCREEN, displayId);
+
+ assertEquals(displayId, motionEvent_Single.getDisplayId());
assertEquals(displayId, motionEvent.getDisplayId());
displayId = 5;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
new file mode 100644
index 0000000..75ca769
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.view.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for TextLanguage.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class TextLanguageTest {
+
+ private static final float EPSILON = 0.000001f;
+
+ @Test
+ public void testParcel() throws Exception {
+ final String bundleKey = "experiment.int";
+ final Bundle bundle = new Bundle();
+ bundle.putInt(bundleKey, 1234);
+
+ final TextLanguage reference = new TextLanguage.Builder()
+ .setId("id")
+ .setExtras(bundle)
+ .putLocale(ULocale.ENGLISH, 0.8f)
+ .putLocale(ULocale.GERMAN, 0.2f)
+ .build();
+
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ final TextLanguage result = TextLanguage.CREATOR.createFromParcel(parcel);
+
+ assertEquals("id", result.getId());
+ assertEquals(1234, result.getExtras().getInt(bundleKey));
+ assertEquals(2, result.getLocaleHypothesisCount());
+ assertEquals(ULocale.ENGLISH, result.getLocale(0));
+ assertEquals(0.8f, result.getConfidenceScore(ULocale.ENGLISH), EPSILON);
+ assertEquals(ULocale.GERMAN, result.getLocale(1));
+ assertEquals(0.2f, result.getConfidenceScore(ULocale.GERMAN), EPSILON);
+ }
+
+ @Test
+ public void testRequestParcel() throws Exception {
+ final String text = "This is random text";
+ final String bundleKey = "experiment.str";
+ final Bundle bundle = new Bundle();
+ bundle.putString(bundleKey, "bundle");
+
+ final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
+ .setExtras(bundle)
+ .build();
+
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ final TextLanguage.Request result = TextLanguage.Request.CREATOR.createFromParcel(parcel);
+
+ assertEquals(text, result.getText());
+ assertEquals("bundle", result.getExtras().getString(bundleKey));
+ }
+}
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 1097bc7..01382aa 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -17,14 +17,9 @@
package android.content.pm;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.content.Context;
import android.os.FileUtils;
-import android.os.Process;
import android.os.ServiceManager;
import android.os.UserManager;
import android.support.test.InstrumentationRegistry;
@@ -32,7 +27,6 @@
import android.util.Log;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -86,12 +80,23 @@
}
@Test
+ public void testSharedInstalledPrimary() throws Exception {
+ assertEquals("1001", getContent(getKernelPackageFile("shared:android.uid.phone", "appid")));
+ }
+
+ @Test
public void testInstalledAll() throws Exception {
assertEquals("", getContent(getKernelPackageFile("com.android.settings",
"excluded_userids")));
}
@Test
+ public void testSharedInstalledAll() throws Exception {
+ assertEquals("", getContent(getKernelPackageFile("shared:android.uid.phone",
+ "excluded_userids")));
+ }
+
+ @Test
public void testNotInstalledSecondary() throws Exception {
mSecondaryUser = getUserManager().createUser("Secondary", 0);
assertEquals(Integer.toString(mSecondaryUser.id),
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 0885a05..ea0a109 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -549,7 +549,7 @@
contextStart - paraStart,
contextEnd - contextStart,
x, y, isRtl, paint.getNativeInstance(),
- mp.getNativeMeasuredParagraph().getNativePtr());
+ mp.getMeasuredText().getNativePtr());
return;
}
}
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index fb30ca2..4de7ca7 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -520,7 +520,7 @@
contextStart - paraStart,
contextEnd - contextStart,
x, y, isRtl, paint.getNativeInstance(),
- mp.getNativeMeasuredParagraph().getNativePtr());
+ mp.getMeasuredText().getNativePtr());
return;
}
}
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index b65fb9c..2e1d81a 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -472,8 +472,8 @@
* <tr>
* <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.039 \\\
- * \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.039 \end{cases}
+ * C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.04045 \\\
+ * \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
* \end{equation}\)
* </td>
* </tr>
@@ -1484,7 +1484,7 @@
"Display P3",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
ILLUMINANT_D65,
- new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.039, 2.4),
+ new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
Named.DISPLAY_P3.ordinal()
);
sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index f291e27..ea93501 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Size;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
@@ -168,4 +169,14 @@
x = in.readInt();
y = in.readInt();
}
+
+ /** {@hide} */
+ public static @NonNull Point convert(@NonNull Size size) {
+ return new Point(size.getWidth(), size.getHeight());
+ }
+
+ /** {@hide} */
+ public static @NonNull Size convert(@NonNull Point point) {
+ return new Size(point.x, point.y);
+ }
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 7ad207f..ba47300 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
+import android.graphics.fonts.FontStyle;
import android.graphics.fonts.FontVariationAxis;
import android.graphics.fonts.SystemFonts;
import android.net.Uri;
@@ -148,7 +149,7 @@
@UnsupportedAppUsage
private @Style int mStyle = 0;
- private @IntRange(from = 0, to = android.graphics.fonts.Font.FONT_WEIGHT_MAX) int mWeight = 0;
+ private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) 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
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index bd1ac25..f426b2d 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -51,61 +51,6 @@
private static final int STYLE_NORMAL = 0;
/**
- * A minimum weight value for the font
- */
- public static final int FONT_WEIGHT_MIN = 1;
-
- /**
- * A font weight value for the thin weight
- */
- public static final int FONT_WEIGHT_THIN = 100;
-
- /**
- * A font weight value for the extra-light weight
- */
- public static final int FONT_WEIGHT_EXTRA_LIGHT = 200;
-
- /**
- * A font weight value for the light weight
- */
- public static final int FONT_WEIGHT_LIGHT = 300;
-
- /**
- * A font weight value for the normal weight
- */
- public static final int FONT_WEIGHT_NORMAL = 400;
-
- /**
- * A font weight value for the medium weight
- */
- public static final int FONT_WEIGHT_MEDIUM = 500;
-
- /**
- * A font weight value for the semi-bold weight
- */
- public static final int FONT_WEIGHT_SEMI_BOLD = 600;
-
- /**
- * A font weight value for the bold weight.
- */
- public static final int FONT_WEIGHT_BOLD = 700;
-
- /**
- * A font weight value for the extra-bold weight
- */
- public static final int FONT_WEIGHT_EXTRA_BOLD = 800;
-
- /**
- * A font weight value for the black weight
- */
- public static final int FONT_WEIGHT_BLACK = 900;
-
- /**
- * A maximum weight value for the font
- */
- public static final int FONT_WEIGHT_MAX = 1000;
-
- /**
* A builder class for creating new Font.
*/
public static class Builder {
@@ -275,66 +220,68 @@
* <tr>
* <td align="center">100</td>
* <td align="center">Thin</td>
- * <td align="center">{@link Font#FONT_WEIGHT_THIN}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_THIN}</td>
* </tr>
* <tr>
* <td align="center">200</td>
* <td align="center">Extra Light (Ultra Light)</td>
- * <td align="center">{@link Font#FONT_WEIGHT_EXTRA_LIGHT}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_LIGHT}</td>
* </tr>
* <tr>
* <td align="center">300</td>
* <td align="center">Light</td>
- * <td align="center">{@link Font#FONT_WEIGHT_LIGHT}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_LIGHT}</td>
* </tr>
* <tr>
* <td align="center">400</td>
* <td align="center">Normal (Regular)</td>
- * <td align="center">{@link Font#FONT_WEIGHT_NORMAL}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_NORMAL}</td>
* </tr>
* <tr>
* <td align="center">500</td>
* <td align="center">Medium</td>
- * <td align="center">{@link Font#FONT_WEIGHT_MEDIUM}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_MEDIUM}</td>
* </tr>
* <tr>
* <td align="center">600</td>
* <td align="center">Semi Bold (Demi Bold)</td>
- * <td align="center">{@link Font#FONT_WEIGHT_SEMI_BOLD}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_SEMI_BOLD}</td>
* </tr>
* <tr>
* <td align="center">700</td>
* <td align="center">Bold</td>
- * <td align="center">{@link Font#FONT_WEIGHT_BOLD}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_BOLD}</td>
* </tr>
* <tr>
* <td align="center">800</td>
* <td align="center">Extra Bold (Ultra Bold)</td>
- * <td align="center">{@link Font#FONT_WEIGHT_EXTRA_BOLD}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_BOLD}</td>
* </tr>
* <tr>
* <td align="center">900</td>
* <td align="center">Black (Heavy)</td>
- * <td align="center">{@link Font#FONT_WEIGHT_BLACK}</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_BLACK}</td>
* </tr>
* </tbody>
* </p>
*
- * @see Font#FONT_WEIGHT_THIN
- * @see Font#FONT_WEIGHT_EXTRA_LIGHT
- * @see Font#FONT_WEIGHT_LIGHT
- * @see Font#FONT_WEIGHT_NORMAL
- * @see Font#FONT_WEIGHT_MEDIUM
- * @see Font#FONT_WEIGHT_SEMI_BOLD
- * @see Font#FONT_WEIGHT_BOLD
- * @see Font#FONT_WEIGHT_EXTRA_BOLD
- * @see Font#FONT_WEIGHT_BLACK
+ * @see FontStyle#FONT_WEIGHT_THIN
+ * @see FontStyle#FONT_WEIGHT_EXTRA_LIGHT
+ * @see FontStyle#FONT_WEIGHT_LIGHT
+ * @see FontStyle#FONT_WEIGHT_NORMAL
+ * @see FontStyle#FONT_WEIGHT_MEDIUM
+ * @see FontStyle#FONT_WEIGHT_SEMI_BOLD
+ * @see FontStyle#FONT_WEIGHT_BOLD
+ * @see FontStyle#FONT_WEIGHT_EXTRA_BOLD
+ * @see FontStyle#FONT_WEIGHT_BLACK
* @param weight a weight value
* @return this builder
*/
public @NonNull Builder setWeight(
- @IntRange(from = FONT_WEIGHT_MIN, to = FONT_WEIGHT_MAX) int weight) {
- Preconditions.checkArgument(FONT_WEIGHT_MIN <= weight && weight <= FONT_WEIGHT_MAX);
+ @IntRange(from = FontStyle.FONT_WEIGHT_MIN, to = FontStyle.FONT_WEIGHT_MAX)
+ int weight) {
+ Preconditions.checkArgument(
+ FontStyle.FONT_WEIGHT_MIN <= weight && weight <= FontStyle.FONT_WEIGHT_MAX);
mWeight = weight;
return this;
}
@@ -346,13 +293,12 @@
* will resolve the style by reading font tables.
*
* For example, if you want to use italic font as upright font, call {@code
- * setItalic(false)} explicitly.
+ * setSlant(false)} explicitly.
*
- * @param italic {@code true} if the font is italic. Otherwise {@code false}.
* @return this builder
*/
- public @NonNull Builder setItalic(boolean italic) {
- mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
+ public @NonNull Builder setSlant(@FontStyle.FontSlant int slant) {
+ mItalic = slant == FontStyle.FONT_SLANT_UPRIGHT ? STYLE_NORMAL : STYLE_ITALIC;
return this;
}
@@ -414,8 +360,11 @@
mItalic = STYLE_NORMAL;
}
}
- mWeight = Math.max(FONT_WEIGHT_MIN, Math.min(FONT_WEIGHT_MAX, mWeight));
+ mWeight = Math.max(FontStyle.FONT_WEIGHT_MIN,
+ Math.min(FontStyle.FONT_WEIGHT_MAX, mWeight));
final boolean italic = (mItalic == STYLE_ITALIC);
+ final int slant = (mItalic == STYLE_ITALIC)
+ ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
final long builderPtr = nInitBuilder();
if (mAxes != null) {
for (FontVariationAxis axis : mAxes) {
@@ -424,8 +373,8 @@
}
final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer();
final long ptr = nBuild(builderPtr, readonlyBuffer, mWeight, italic, mTtcIndex);
- final Font font = new Font(ptr, readonlyBuffer, mFile, mWeight, italic, mTtcIndex,
- mAxes, mLocaleList);
+ final Font font = new Font(ptr, readonlyBuffer, mFile,
+ new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList);
sFontRegistory.registerNativeAllocation(font, ptr);
return font;
}
@@ -454,8 +403,7 @@
private final long mNativePtr; // address of the shared ptr of minikin::Font
private final @NonNull ByteBuffer mBuffer;
private final @Nullable File mFile;
- private final @IntRange(from = 0, to = 1000) int mWeight;
- private final boolean mItalic;
+ private final FontStyle mFontStyle;
private final @IntRange(from = 0) int mTtcIndex;
private final @Nullable FontVariationAxis[] mAxes;
private final @NonNull String mLocaleList;
@@ -464,13 +412,11 @@
* Use Builder instead
*/
private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
- @IntRange(from = 0, to = 1000) int weight, boolean italic,
- @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes,
- @NonNull String localeList) {
+ @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex,
+ @Nullable FontVariationAxis[] axes, @NonNull String localeList) {
mBuffer = buffer;
mFile = file;
- mWeight = weight;
- mItalic = italic;
+ mFontStyle = fontStyle;
mNativePtr = nativePtr;
mTtcIndex = ttcIndex;
mAxes = axes;
@@ -504,17 +450,17 @@
* @return a weight value
*/
public @IntRange(from = 0, to = 1000)int getWeight() {
- return mWeight;
+ return mFontStyle.getWeight();
}
/**
- * Returns true if this font is marked as italic, otherwise returns false.
+ * Get a slant value associated with this font.
*
- * @see Builder#setItalic(boolean)
- * @return true if italic, otherwise false
+ * @see Builder#setSlant(boolean)
+ * @return a slant value
*/
- public boolean isItalic() {
- return mItalic;
+ public @FontStyle.FontSlant int getSlant() {
+ return mFontStyle.getSlant();
}
/**
@@ -564,21 +510,20 @@
return false;
}
Font f = (Font) o;
- return f.mWeight == mWeight && f.mItalic == mItalic && f.mTtcIndex == mTtcIndex
+ return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
&& Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer);
}
@Override
public int hashCode() {
- return Objects.hash(mWeight, mItalic, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
+ return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
}
@Override
public String toString() {
return "Font {"
+ "path=" + mFile
- + ", weight=" + mWeight
- + ", italic=" + mItalic
+ + ", style=" + mFontStyle
+ ", ttcIndex=" + mTtcIndex
+ ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
+ ", localeList=" + mLocaleList
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 3bcdc31..52a37da 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -124,7 +124,7 @@
}
private static int makeStyleIdentifier(@NonNull Font font) {
- return font.getWeight() | (font.isItalic() ? (1 << 16) : 0);
+ return font.getWeight() | (font.getSlant() << 16);
}
private static native long nInitBuilder();
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
new file mode 100644
index 0000000..82fc7ac
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -0,0 +1,256 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A font style object.
+ *
+ * This class represents a single font style which is a pair of weight value and slant value.
+ * Here are common font styles examples:
+ * <p>
+ * <pre>
+ * <code>
+ * final FontStyle NORMAL = new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT);
+ * final FontStyle BOLD = new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT);
+ * final FontStyle ITALIC = new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC);
+ * final FontStyle BOLD_ITALIC = new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_ITALIC);
+ * </code>
+ * </pre>
+ * </p>
+ *
+ */
+public final class FontStyle {
+ private static final String TAG = "FontStyle";
+
+ /**
+ * A minimum weight value for the font
+ */
+ public static final int FONT_WEIGHT_MIN = 1;
+
+ /**
+ * A font weight value for the thin weight
+ */
+ public static final int FONT_WEIGHT_THIN = 100;
+
+ /**
+ * A font weight value for the extra-light weight
+ */
+ public static final int FONT_WEIGHT_EXTRA_LIGHT = 200;
+
+ /**
+ * A font weight value for the light weight
+ */
+ public static final int FONT_WEIGHT_LIGHT = 300;
+
+ /**
+ * A font weight value for the normal weight
+ */
+ public static final int FONT_WEIGHT_NORMAL = 400;
+
+ /**
+ * A font weight value for the medium weight
+ */
+ public static final int FONT_WEIGHT_MEDIUM = 500;
+
+ /**
+ * A font weight value for the semi-bold weight
+ */
+ public static final int FONT_WEIGHT_SEMI_BOLD = 600;
+
+ /**
+ * A font weight value for the bold weight.
+ */
+ public static final int FONT_WEIGHT_BOLD = 700;
+
+ /**
+ * A font weight value for the extra-bold weight
+ */
+ public static final int FONT_WEIGHT_EXTRA_BOLD = 800;
+
+ /**
+ * A font weight value for the black weight
+ */
+ public static final int FONT_WEIGHT_BLACK = 900;
+
+ /**
+ * A maximum weight value for the font
+ */
+ public static final int FONT_WEIGHT_MAX = 1000;
+
+ /**
+ * A font slant value for upright
+ */
+ public static final int FONT_SLANT_UPRIGHT = 0;
+
+ /**
+ * A font slant value for italic
+ */
+ public static final int FONT_SLANT_ITALIC = 1;
+
+ // TODO: Support FONT_SLANT_OBLIQUE
+
+ /** @hide */
+ @IntDef(prefix = { "FONT_SLANT_" }, value = {
+ FONT_SLANT_UPRIGHT,
+ FONT_SLANT_ITALIC
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FontSlant {}
+
+ private final @IntRange(from = 0, to = 1000) int mWeight;
+ private final @FontSlant int mSlant;
+ // TODO: Support width
+
+ public FontStyle() {
+ mWeight = FONT_WEIGHT_NORMAL;
+ mSlant = FONT_SLANT_UPRIGHT;
+ }
+
+ /**
+ * Create FontStyle with specific weight and italic
+ *
+ * <p>
+ * <table>
+ * <thead>
+ * <tr>
+ * <th align="center">Value</th>
+ * <th align="center">Name</th>
+ * <th align="center">Android Definition</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td align="center">100</td>
+ * <td align="center">Thin</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_THIN}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">200</td>
+ * <td align="center">Extra Light (Ultra Light)</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">300</td>
+ * <td align="center">Light</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">400</td>
+ * <td align="center">Normal (Regular)</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_NORMAL}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">500</td>
+ * <td align="center">Medium</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_MEDIUM}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">600</td>
+ * <td align="center">Semi Bold (Demi Bold)</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_SEMI_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">700</td>
+ * <td align="center">Bold</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">800</td>
+ * <td align="center">Extra Bold (Ultra Bold)</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">900</td>
+ * <td align="center">Black (Heavy)</td>
+ * <td align="center">{@link FontStyle#FONT_WEIGHT_BLACK}</td>
+ * </tr>
+ * </tbody>
+ * </p>
+ *
+ * @see FontStyle#FONT_WEIGHT_THIN
+ * @see FontStyle#FONT_WEIGHT_EXTRA_LIGHT
+ * @see FontStyle#FONT_WEIGHT_LIGHT
+ * @see FontStyle#FONT_WEIGHT_NORMAL
+ * @see FontStyle#FONT_WEIGHT_MEDIUM
+ * @see FontStyle#FONT_WEIGHT_SEMI_BOLD
+ * @see FontStyle#FONT_WEIGHT_BOLD
+ * @see FontStyle#FONT_WEIGHT_EXTRA_BOLD
+ * @see FontStyle#FONT_WEIGHT_BLACK
+ * @param weight a weight value
+ * @param slant a slant value
+ */
+ public FontStyle(int weight, @FontSlant int slant) {
+ Preconditions.checkArgument(FONT_WEIGHT_MIN <= weight && weight <= FONT_WEIGHT_MAX,
+ "weight value must be [" + FONT_WEIGHT_MIN + ", " + FONT_WEIGHT_MAX + "]");
+ Preconditions.checkArgument(slant == FONT_SLANT_UPRIGHT || slant == FONT_SLANT_ITALIC,
+ "slant value must be FONT_SLANT_UPRIGHT or FONT_SLANT_UPRIGHT");
+ mWeight = weight;
+ mSlant = slant;
+ }
+
+
+ /**
+ * Gets the weight value
+ *
+ * @see FontStyle#setWeight(int)
+ * @return a weight value
+ */
+ public @IntRange(from = 0, to = 1000) int getWeight() {
+ return mWeight;
+ }
+
+ /**
+ * Gets the slant value
+ *
+ * @return a slant value
+ */
+ public @FontSlant int getSlant() {
+ return mSlant;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null || !(o instanceof FontStyle)) {
+ return false;
+ }
+ FontStyle fontStyle = (FontStyle) o;
+ return fontStyle.mWeight == mWeight && fontStyle.mSlant == mSlant;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWeight, mSlant);
+ }
+
+ @Override
+ public String toString() {
+ return "FontStyle { weight=" + mWeight + ", slant=" + mSlant + "}";
+ }
+}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 2d21bbb..750adb2 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -192,7 +192,8 @@
try {
font = new Font.Builder(buffer, new File(fullPath), languageTags)
.setWeight(fontConfig.getWeight())
- .setItalic(fontConfig.isItalic())
+ .setSlant(fontConfig.isItalic() ? FontStyle.FONT_SLANT_ITALIC
+ : FontStyle.FONT_SLANT_UPRIGHT)
.setTtcIndex(fontConfig.getTtcIndex())
.setFontVariationSettings(fontConfig.getAxes())
.build();
diff --git a/core/java/android/text/NativeLineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
similarity index 67%
rename from core/java/android/text/NativeLineBreaker.java
rename to graphics/java/android/graphics/text/LineBreaker.java
index 94e10e8..1647909 100644
--- a/core/java/android/text/NativeLineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.text;
+package android.graphics.text;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -32,11 +32,60 @@
import java.lang.annotation.RetentionPolicy;
/**
- * A native implementation of the line breaker.
- * TODO: Consider to make this class public.
- * @hide
+ * Provides automatic line breaking for a <em>single</em> paragraph.
+ *
+ * <p>
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
+ * String text = "Hello, Android.";
+ *
+ * // Prepare the measured text
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ * .appendStyleRun(paint, 7, false) // Use paint for "Hello, "
+ * .appednStyleRun(bigPaint, 8, false) // Use bigPaint for "Hello, "
+ * .build();
+ *
+ * LineBreaker lb = new LineBreaker.Builder()
+ * // Use simple line breaker
+ * .setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE)
+ * // Do not add hyphenation.
+ * .setHyphenationFrequency(LineBreaker.HYPHENATION_FREQUENCY_NONE)
+ * // Build the LineBreaker
+ * .build();
+ *
+ * ParagraphConstraints c = new ParagraphConstraints();
+ * c.setWidth(240); // Set the line wieth as 1024px
+ *
+ * // Do the line breaking
+ * Result r = lb.computeLineBreaks(mt, c, 0);
+ *
+ * // Compute the total height of the text.
+ * float totalHeight = 0;
+ * for (int i = 0; i < r.getLineCount(); ++i) { // iterate over the lines
+ * totalHeight += r.getLineDescent(i) - r.getLineAscent(i);
+ * }
+ *
+ * // Draw text to the canvas
+ * Bitmap bmp = new Bitmap.createBitmap(240, totalHeight, Bitmap.Config.ARGB_8888);
+ * Canvas c = new Canvas(bmp);
+ * float yOffset = 0f;
+ * int prevOffset = 0;
+ * for (int i = 0; i < r.getLineCount(); ++i) { // iterate over the lines
+ * int nextOffset = r.getLineBreakOffset(i);
+ * c.drawText(text, prevOffset, nextOffset, 0f, yOffset, paint);
+ *
+ * prevOffset = nextOffset;
+ * yOffset += r.getLineDescent(i) - r.getLineAscent(i);
+ * }
+ * </code>
+ * </pre>
+ * </p>
*/
-public class NativeLineBreaker {
+public class LineBreaker {
+ /** @hide */
@IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
BREAK_STRATEGY_SIMPLE,
BREAK_STRATEGY_HIGH_QUALITY,
@@ -46,25 +95,33 @@
public @interface BreakStrategy {}
/**
- * Value for break strategy indicating simple line breaking. Automatic hyphens are not added
- * (though soft hyphens are respected), and modifying text generally doesn't affect the layout
- * before it (which yields a more consistent user experience when editing), but layout may not
- * be the highest quality.
+ * Value for break strategy indicating simple line breaking.
+ *
+ * The line breaker puts words to the line as much as possible and breaks line if no more words
+ * can fit into the same line. Automatic hyphens are only added when a line has a single word
+ * and that word is longer than line width. This is the fastest break strategy and ideal for
+ * editor.
*/
public static final int BREAK_STRATEGY_SIMPLE = 0;
/**
- * Value for break strategy indicating high quality line breaking, including automatic
- * hyphenation and doing whole-paragraph optimization of line breaks.
+ * Value for break strategy indicating high quality line breaking.
+ *
+ * With this option line breaker does whole-paragraph optimization for more readable text, and
+ * also applies automatic hyphenation when required.
*/
public static final int BREAK_STRATEGY_HIGH_QUALITY = 1;
/**
- * Value for break strategy indicating balanced line breaking. The breaks are chosen to
- * make all lines as close to the same length as possible, including automatic hyphenation.
+ * Value for break strategy indicating balanced line breaking.
+ *
+ * The line breaker does whole-paragraph optimization for making all lines similar length, and
+ * also applies automatic hyphenation when required. This break strategy is good for small
+ * screen devices such as watch screens.
*/
public static final int BREAK_STRATEGY_BALANCED = 2;
+ /** @hide */
@IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
HYPHENATION_FREQUENCY_NORMAL,
HYPHENATION_FREQUENCY_FULL,
@@ -74,28 +131,32 @@
public @interface HyphenationFrequency {}
/**
- * Value for hyphenation frequency indicating no automatic hyphenation. Useful
- * for backward compatibility, and for cases where the automatic hyphenation algorithm results
- * in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the
- * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used
- * as suggestions for potential line breaks.
+ * Value for hyphenation frequency indicating no automatic hyphenation.
+ *
+ * Using this option disables auto hyphenation which results in better text layout performance.
+ * A word may be broken without hyphens when a line has a single word and that word is longer
+ * than line width. Soft hyphens are ignored and will not be used as suggestions for potential
+ * line breaks.
*/
public static final int HYPHENATION_FREQUENCY_NONE = 0;
/**
- * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which
- * is a conservative default. Useful for informal cases, such as short sentences or chat
+ * Value for hyphenation frequency indicating a light amount of automatic hyphenation.
+ *
+ * This hyphenation frequency is useful for informal cases, such as short sentences or chat
* messages.
*/
public static final int HYPHENATION_FREQUENCY_NORMAL = 1;
/**
- * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical
- * in typography. Useful for running text and where it's important to put the maximum amount of
- * text in a screen with limited space.
+ * Value for hyphenation frequency indicating the full amount of automatic hyphenation.
+ *
+ * This hyphenation frequency is useful for running text and where it's important to put the
+ * maximum amount of text in a screen with limited space.
*/
public static final int HYPHENATION_FREQUENCY_FULL = 2;
+ /** @hide */
@IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
JUSTIFICATION_MODE_NONE,
JUSTIFICATION_MODE_INTER_WORD
@@ -114,7 +175,7 @@
public static final int JUSTIFICATION_MODE_INTER_WORD = 1;
/**
- * A builder class of NativeLineBreaker.
+ * Helper class for creating a {@link LineBreaker}.
*/
public static class Builder {
private @BreakStrategy int mBreakStrategy = BREAK_STRATEGY_SIMPLE;
@@ -123,12 +184,10 @@
private @Nullable int[] mIndents = null;
/**
- * Construct a builder class.
- */
- public Builder() {}
-
- /**
* Set break strategy.
+ *
+ * You can change the line breaking behavior by setting break strategy. The default value is
+ * {@link #BREAK_STRATEGY_SIMPLE}.
*/
public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
mBreakStrategy = breakStrategy;
@@ -137,6 +196,9 @@
/**
* Set hyphenation frequency.
+ *
+ * You can change the amount of automatic hyphenation used. The default value is
+ * {@link #HYPHENATION_FREQUENCY_NONE}.
*/
public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
mHyphenationFrequency = hyphenationFrequency;
@@ -145,6 +207,10 @@
/**
* Set whether the text is justified.
+ *
+ * By setting {@link #JUSTIFICATION_MODE_INTER_WORD}, the line breaker will change the
+ * internal parameters for justification.
+ * The default value is {@link #JUSTIFICATION_MODE_NONE}
*/
public Builder setJustified(@JustificationMode int justified) {
mJustified = justified;
@@ -152,9 +218,11 @@
}
/**
- * Set indents for entire text.
+ * Set indents.
*
- * Sets the total (left + right) indents in pixel per lines.
+ * The supplied array provides the total amount of indentation per line, in pixel. This
+ * amount is the sum of both left and right indentations. For lines past the last element in
+ * the array, the indentation amount of the last element is used.
*/
public Builder setIndents(@Nullable int[] indents) {
mIndents = indents;
@@ -162,11 +230,12 @@
}
/**
- * Returns the NativeLineBreaker with given parameters.
+ * Build a new LineBreaker with given parameters.
+ *
+ * You can reuse the Builder instance even after calling this method.
*/
- NativeLineBreaker build() {
- return new NativeLineBreaker(mBreakStrategy, mHyphenationFrequency, mJustified,
- mIndents);
+ public LineBreaker build() {
+ return new LineBreaker(mBreakStrategy, mHyphenationFrequency, mJustified, mIndents);
}
}
@@ -184,8 +253,10 @@
/**
* Set width for this paragraph.
+ *
+ * @see #getWidth()
*/
- public void setWidth(@FloatRange(from = 0.0f) float width) {
+ public void setWidth(@Px @FloatRange(from = 0.0f) float width) {
mWidth = width;
}
@@ -194,9 +265,11 @@
*
* @param firstWidth the line width of the starting of the paragraph
* @param firstWidthLineCount the number of lines that applies the firstWidth
+ * @see #getFirstWidth()
+ * @see #getFirstWidthLineCount()
*/
- public void setIndent(@FloatRange(from = 0.0f) float firstWidth,
- @IntRange(from = 0) int firstWidthLineCount) {
+ public void setIndent(@Px @FloatRange(from = 0.0f) float firstWidth,
+ @Px @IntRange(from = 0) int firstWidthLineCount) {
mFirstWidth = firstWidth;
mFirstWidthLineCount = firstWidthLineCount;
}
@@ -206,16 +279,21 @@
*
* @param tabStops the array of pixels of tap stopping position
* @param defaultTabStop pixels of the default tab stopping position
+ * @see #getTabStops()
+ * @see #getDefaultTabStop()
*/
- public void setTabStops(@Nullable int[] tabStops, @IntRange(from = 0) int defaultTabStop) {
+ public void setTabStops(@Nullable int[] tabStops,
+ @Px @IntRange(from = 0) int defaultTabStop) {
mVariableTabStops = tabStops;
mDefaultTabStop = defaultTabStop;
}
/**
* Return the width for this paragraph in pixels.
+ *
+ * @see #setWidth(float)
*/
- public @FloatRange(from = 0.0f) float getWidth() {
+ public @Px @FloatRange(from = 0.0f) float getWidth() {
return mWidth;
}
@@ -224,7 +302,7 @@
*
* @see #setIndent(float, int)
*/
- public @FloatRange(from = 0.0f) float getFirstWidth() {
+ public @Px @FloatRange(from = 0.0f) float getFirstWidth() {
return mFirstWidth;
}
@@ -233,7 +311,7 @@
*
* @see #setIndent(float, int)
*/
- public @IntRange(from = 0) int getFirstWidthLineCount() {
+ public @Px @IntRange(from = 0) int getFirstWidthLineCount() {
return mFirstWidthLineCount;
}
@@ -251,13 +329,14 @@
*
* @see #setTabStop(int[], int)
*/
- public @IntRange(from = 0) int getDefaultTabStop() {
+ public @Px @IntRange(from = 0) int getDefaultTabStop() {
return mDefaultTabStop;
}
}
/**
- * A result object of a line breaking
+ * Holds the result of the {@link LineBreaker#computeLineBreaks line breaking algorithm}.
+ * @see LineBreaker#computeLineBreaks
*/
public static class Result {
// Following two contstant must be synced with minikin's line breaker.
@@ -274,7 +353,7 @@
}
/**
- * Returns a number of line count.
+ * Returns the number of lines in the paragraph.
*
* @return number of lines
*/
@@ -283,7 +362,7 @@
}
/**
- * Returns a break offset of the line.
+ * Returns character offset of the break for a given line.
*
* @param lineIndex an index of the line.
* @return the break offset.
@@ -293,17 +372,17 @@
}
/**
- * Returns a width of the line in pixels.
+ * Returns width of a given line in pixels.
*
* @param lineIndex an index of the line.
- * @return a width of the line in pixexls
+ * @return width of the line in pixels
*/
public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) {
return nGetLineWidth(mPtr, lineIndex);
}
/**
- * Returns an entier font ascent of the line in pixels.
+ * Returns font ascent of the line in pixels.
*
* @param lineIndex an index of the line.
* @return an entier font ascent of the line in pixels.
@@ -313,7 +392,7 @@
}
/**
- * Returns an entier font descent of the line in pixels.
+ * Returns font descent of the line in pixels.
*
* @param lineIndex an index of the line.
* @return an entier font descent of the line in pixels.
@@ -337,6 +416,7 @@
*
* @param lineIndex an index of the line.
* @return a packed hyphen edit for the line.
+ *
* @see android.text.Hyphenator#unpackStartHyphenEdit(int)
* @see android.text.Hyphenator#unpackEndHyphenEdit(int)
* @see android.text.Hyphenator#packHyphenEdit(int,int)
@@ -347,14 +427,14 @@
}
private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- NativeLineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
+ LineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
private final long mNativePtr;
/**
* Use Builder instead.
*/
- private NativeLineBreaker(@BreakStrategy int breakStrategy,
+ private LineBreaker(@BreakStrategy int breakStrategy,
@HyphenationFrequency int hyphenationFrequency, @JustificationMode int justify,
@Nullable int[] indents) {
mNativePtr = nInit(breakStrategy, hyphenationFrequency,
@@ -372,7 +452,7 @@
* @param lineNumber a line number of this paragraph
*/
public Result computeLineBreaks(
- @NonNull NativeMeasuredParagraph measuredPara,
+ @NonNull MeasuredText measuredPara,
@NonNull ParagraphConstraints constraints,
@IntRange(from = 0) int lineNumber) {
return new Result(nComputeLineBreaks(
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
new file mode 100644
index 0000000..3efe655
--- /dev/null
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2010 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.graphics.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Result of text shaping of the single paragraph string.
+ *
+ * <p>
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
+ * String text = "Hello, Android.";
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ * .appendStyleRun(paint, 7, false) // Use paint for "Hello, "
+ * .appendStyleRun(bigPaint, 8, false) // Use bigPaint for "Hello, "
+ * .build();
+ * </code>
+ * </pre>
+ * </p>
+ */
+public class MeasuredText {
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+ private long mNativePtr;
+ private @NonNull char[] mChars;
+
+ // Use builder instead.
+ private MeasuredText(long ptr, @NonNull char[] chars) {
+ mNativePtr = ptr;
+ mChars = chars;
+ }
+
+ /**
+ * Returns the characters in the paragraph used to compute this MeasuredText instance.
+ */
+ public @NonNull char[] getChars() {
+ return mChars;
+ }
+
+ /**
+ * Returns the width of a given range.
+ *
+ * @param start an inclusive start index of the range
+ * @param end an exclusive end index of the range
+ */
+ public @FloatRange(from = 0.0) @Px float getWidth(
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+ Preconditions.checkArgument(0 <= start && start <= mChars.length,
+ "start(" + start + ") must be 0 <= start <= " + mChars.length);
+ Preconditions.checkArgument(0 <= end && end <= mChars.length,
+ "end(" + end + ") must be 0 <= end <= " + mChars.length);
+ Preconditions.checkArgument(start <= end,
+ "start(" + start + ") is larger than end(" + end + ")");
+ return nGetWidth(mNativePtr, start, end);
+ }
+
+ /**
+ * Returns a memory usage of the native object.
+ *
+ * @hide
+ */
+ public int getMemoryUsage() {
+ return nGetMemoryUsage(mNativePtr);
+ }
+
+ /**
+ * Retrieves the boundary box of the given range
+ *
+ * @param start an inclusive start index of the range
+ * @param end an exclusive end index of the range
+ * @param rect an output parameter
+ */
+ public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @NonNull Rect rect) {
+ Preconditions.checkArgument(0 <= start && start <= mChars.length,
+ "start(" + start + ") must be 0 <= start <= " + mChars.length);
+ Preconditions.checkArgument(0 <= end && end <= mChars.length,
+ "end(" + end + ") must be 0 <= end <= " + mChars.length);
+ Preconditions.checkArgument(start <= end,
+ "start(" + start + ") is larger than end(" + end + ")");
+ Preconditions.checkNotNull(rect);
+ nGetBounds(mNativePtr, mChars, start, end, rect);
+ }
+
+ /**
+ * Returns the width of the character at the given offset.
+ *
+ * @param offset an offset of the character.
+ */
+ public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
+ Preconditions.checkArgument(0 <= offset && offset < mChars.length,
+ "offset(" + offset + ") is larger than text length: " + mChars.length);
+ return nGetCharWidthAt(mNativePtr, offset);
+ }
+
+ /**
+ * Returns a native pointer of the underlying native object.
+ *
+ * @hide
+ */
+ public long getNativePtr() {
+ return mNativePtr;
+ }
+
+ @CriticalNative
+ private static native float nGetWidth(/* Non Zero */ long nativePtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end);
+
+ @CriticalNative
+ private static native /* Non Zero */ long nGetReleaseFunc();
+
+ @CriticalNative
+ private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+ Rect rect);
+
+ @CriticalNative
+ private static native float nGetCharWidthAt(long nativePtr, int offset);
+
+ /**
+ * Helper class for creating a {@link MeasuredText}.
+ * <p>
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * String text = "Hello, Android.";
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ * .appendStyleRun(paint, text.length, false)
+ * .build();
+ * </code>
+ * </pre>
+ * </p>
+ *
+ * Note: The appendStyle and appendReplacementRun should be called to cover the text length.
+ */
+ public static class Builder {
+ private long mNativePtr;
+
+ private final @NonNull char[] mText;
+ private boolean mComputeHyphenation = false;
+ private boolean mComputeLayout = true;
+ private int mCurrentOffset = 0;
+
+ /**
+ * Construct a builder.
+ *
+ * The MeasuredText returned by build method will hold a reference of the text. Developer is
+ * not supposed to modify the text.
+ *
+ * @param text a text
+ */
+ public Builder(@NonNull char[] text) {
+ Preconditions.checkNotNull(text);
+ mText = text;
+ mNativePtr = nInitBuilder();
+ }
+
+ /**
+ * Apply styles to the given length.
+ *
+ * Keeps an internal offset which increases at every append. The initial value for this
+ * offset is zero. After the style is applied the internal offset is moved to {@code offset
+ * + length}, and next call will start from this new position.
+ *
+ * @param paint a paint
+ * @param length a length to be applied with a given paint, can not exceed the length of the
+ * text
+ * @param isRtl true if the text is in RTL context, otherwise false.
+ */
+ public Builder appendStyleRun(@NonNull Paint paint, @IntRange(from = 0) int length,
+ boolean isRtl) {
+ Preconditions.checkNotNull(paint);
+ Preconditions.checkArgument(length > 0, "length can not be negative");
+ final int end = mCurrentOffset + length;
+ Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, isRtl);
+ mCurrentOffset = end;
+ return this;
+ }
+
+ /**
+ * Used to inform the text layout that the given length is replaced with the object of given
+ * width.
+ *
+ * Keeps an internal offset which increases at every append. The initial value for this
+ * offset is zero. After the style is applied the internal offset is moved to {@code offset
+ * + length}, and next call will start from this new position.
+ *
+ * Informs the layout engine that the given length should not be processed, instead the
+ * provided width should be used for calculating the width of that range.
+ *
+ * @param length a length to be replaced with the object, can not exceed the length of the
+ * text
+ * @param width a replacement width of the range
+ */
+ public Builder appendReplacementRun(@NonNull Paint paint,
+ @IntRange(from = 0) int length, @FloatRange(from = 0) float width) {
+ Preconditions.checkArgument(length > 0, "length can not be negative");
+ final int end = mCurrentOffset + length;
+ Preconditions.checkArgument(end <= mText.length, "Replacement exceeds the text length");
+ nAddReplacementRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, width);
+ mCurrentOffset = end;
+ return this;
+ }
+
+ /**
+ * By passing true to this method, the build method will compute all possible hyphenation
+ * pieces as well.
+ *
+ * If you don't want to use automatic hyphenation, you can pass false to this method and
+ * save the computation time of hyphenation. The default value is false.
+ *
+ * Even if you pass false to this method, you can still enable automatic hyphenation of
+ * LineBreaker but line break computation becomes slower.
+ *
+ * @param computeHyphenation true if you want to use automatic hyphenations.
+ */
+ public Builder setComputeHyphenation(boolean computeHyphenation) {
+ mComputeHyphenation = computeHyphenation;
+ return this;
+ }
+
+ /**
+ * By passing true to this method, the build method will compute all full layout
+ * information.
+ *
+ * If you don't use {@link MeasuredText#getBounds(int,int,android.graphics.Rect)}, you can
+ * pass false to this method and save the memory spaces. The default value is true.
+ *
+ * Even if you pass false to this method, you can still call getBounds but it becomes
+ * slower.
+ *
+ * @param computeLayout true if you want to retrieve full layout info, e.g. bbox.
+ */
+ public Builder setComputeLayout(boolean computeLayout) {
+ mComputeLayout = computeLayout;
+ return this;
+ }
+
+ /**
+ * Creates a MeasuredText.
+ *
+ * Once you called build() method, you can't reuse the Builder class again.
+ * @throws IllegalStateException if this Builder is reused.
+ * @throws IllegalStateException if the whole text is not covered by one or more runs (style
+ * or replacement)
+ */
+ public MeasuredText build() {
+ ensureNativePtrNoReuse();
+ if (mCurrentOffset != mText.length) {
+ throw new IllegalStateException("Style info has not been provided for all text.");
+ }
+ try {
+ long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
+ mComputeLayout);
+ MeasuredText res = new MeasuredText(ptr, mText);
+ sRegistry.registerNativeAllocation(res, ptr);
+ return res;
+ } finally {
+ nFreeBuilder(mNativePtr);
+ mNativePtr = 0;
+ }
+ }
+
+ /**
+ * Ensures {@link #mNativePtr} is not reused.
+ *
+ * <p/> This is a method by itself to help increase testability - eg. Robolectric might want
+ * to override the validation behavior in test environment.
+ */
+ private void ensureNativePtrNoReuse() {
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Builder can not be reused.");
+ }
+ }
+
+ private static native /* Non Zero */ long nInitBuilder();
+
+ /**
+ * Apply style to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param isRtl True if the text is RTL.
+ */
+ private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl);
+ /**
+ * Apply ReplacementRun to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param width The width of the replacement.
+ */
+ private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width);
+
+ private static native long nBuildMeasuredText(
+ /* Non Zero */ long nativeBuilderPtr,
+ @NonNull char[] text,
+ boolean computeHyphenation,
+ boolean computeLayout);
+
+ private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+ }
+}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 8f58f74..66a5477 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -39,7 +39,7 @@
static const std::string kResourcesArsc("resources.arsc");
-ApkAssets::ApkAssets(void* unmanaged_handle, const std::string& path)
+ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path)
: zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) {
}
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 69702e3..db2d038 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -27,6 +27,9 @@
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
+
namespace android {
class LoadedIdmap;
@@ -88,9 +91,9 @@
// Creates an Asset from any file on the file system.
static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
- ApkAssets(void* unmanaged_handle, const std::string& path);
+ ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path);
- using ZipArchivePtr = std::unique_ptr<void, void(*)(void*)>;
+ using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
const std::string path_;
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index 03154d0..c221e3b 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -41,7 +41,8 @@
#include <unistd.h>
#include <time.h>
-typedef void* ZipArchiveHandle;
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
namespace android {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e3ec45b..f0053a4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -202,9 +202,7 @@
"AnimationContext.cpp",
"Animator.cpp",
"AnimatorManager.cpp",
- "CanvasState.cpp",
"CanvasTransform.cpp",
- "ClipArea.cpp",
"DamageAccumulator.cpp",
"DeferredLayerUpdater.cpp",
"DeviceInfo.cpp",
@@ -227,14 +225,15 @@
"RecordingCanvas.cpp",
"RenderNode.cpp",
"RenderProperties.cpp",
- "ResourceCache.cpp",
"SkiaCanvas.cpp",
- "Snapshot.cpp",
"TreeInfo.cpp",
"VectorDrawable.cpp",
"protos/graphicsstats.proto",
],
+ // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
+ cflags: ["-Wno-implicit-fallthrough"],
+
proto: {
export_proto_headers: true,
},
@@ -308,8 +307,6 @@
"tests/unit/main.cpp",
"tests/unit/CacheManagerTests.cpp",
"tests/unit/CanvasContextTests.cpp",
- "tests/unit/CanvasStateTests.cpp",
- "tests/unit/ClipAreaTests.cpp",
"tests/unit/DamageAccumulatorTests.cpp",
"tests/unit/DeferredLayerUpdaterTests.cpp",
"tests/unit/FatVectorTests.cpp",
@@ -328,7 +325,6 @@
"tests/unit/SkiaPipelineTests.cpp",
"tests/unit/SkiaRenderPropertiesTests.cpp",
"tests/unit/SkiaCanvasTests.cpp",
- "tests/unit/SnapshotTests.cpp",
"tests/unit/StringUtilsTests.cpp",
"tests/unit/TestUtilsTests.cpp",
"tests/unit/ThreadBaseTests.cpp",
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
deleted file mode 100644
index d18c4ab..0000000
--- a/libs/hwui/CanvasState.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CanvasState.h"
-#include "hwui/Canvas.h"
-#include "utils/MathUtils.h"
-
-namespace android {
-namespace uirenderer {
-
-CanvasState::CanvasState(CanvasStateClient& renderer)
- : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {}
-
-CanvasState::~CanvasState() {
- // First call freeSnapshot on all but mFirstSnapshot
- // to invoke all the dtors
- freeAllSnapshots();
-
- // Now actually release the memory
- while (mSnapshotPool) {
- void* temp = mSnapshotPool;
- mSnapshotPool = mSnapshotPool->previous;
- free(temp);
- }
-}
-
-void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) {
- if (mWidth != viewportWidth || mHeight != viewportHeight) {
- mWidth = viewportWidth;
- mHeight = viewportHeight;
- mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
- mCanvas.onViewportInitialized();
- }
-
- freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
- mSnapshot->setRelativeLightCenter(Vector3());
- mSaveCount = 1;
-}
-
-void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft,
- float clipTop, float clipRight, float clipBottom,
- const Vector3& lightCenter) {
- if (mWidth != viewportWidth || mHeight != viewportHeight) {
- mWidth = viewportWidth;
- mHeight = viewportHeight;
- mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
- mCanvas.onViewportInitialized();
- }
-
- freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
- mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
- mSnapshot->fbo = mCanvas.getTargetFbo();
- mSnapshot->setRelativeLightCenter(lightCenter);
- mSaveCount = 1;
-}
-
-Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
- void* memory;
- if (mSnapshotPool) {
- memory = mSnapshotPool;
- mSnapshotPool = mSnapshotPool->previous;
- mSnapshotPoolCount--;
- } else {
- memory = malloc(sizeof(Snapshot));
- }
- return new (memory) Snapshot(previous, savecount);
-}
-
-void CanvasState::freeSnapshot(Snapshot* snapshot) {
- snapshot->~Snapshot();
- // Arbitrary number, just don't let this grown unbounded
- if (mSnapshotPoolCount > 10) {
- free((void*)snapshot);
- } else {
- snapshot->previous = mSnapshotPool;
- mSnapshotPool = snapshot;
- mSnapshotPoolCount++;
- }
-}
-
-void CanvasState::freeAllSnapshots() {
- while (mSnapshot != &mFirstSnapshot) {
- Snapshot* temp = mSnapshot;
- mSnapshot = mSnapshot->previous;
- freeSnapshot(temp);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Save (layer)
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Guaranteed to save without side-effects
- *
- * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save
- * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
- */
-int CanvasState::saveSnapshot(int flags) {
- mSnapshot = allocSnapshot(mSnapshot, flags);
- return mSaveCount++;
-}
-
-int CanvasState::save(int flags) {
- return saveSnapshot(flags);
-}
-
-/**
- * Guaranteed to restore without side-effects.
- */
-void CanvasState::restoreSnapshot() {
- Snapshot* toRemove = mSnapshot;
- Snapshot* toRestore = mSnapshot->previous;
-
- mSaveCount--;
- mSnapshot = toRestore;
-
- // subclass handles restore implementation
- mCanvas.onSnapshotRestored(*toRemove, *toRestore);
-
- freeSnapshot(toRemove);
-}
-
-void CanvasState::restore() {
- if (mSaveCount > 1) {
- restoreSnapshot();
- }
-}
-
-void CanvasState::restoreToCount(int saveCount) {
- if (saveCount < 1) saveCount = 1;
-
- while (mSaveCount > saveCount) {
- restoreSnapshot();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Matrix
-///////////////////////////////////////////////////////////////////////////////
-
-void CanvasState::getMatrix(SkMatrix* matrix) const {
- mSnapshot->transform->copyTo(*matrix);
-}
-
-void CanvasState::translate(float dx, float dy, float dz) {
- mSnapshot->transform->translate(dx, dy, dz);
-}
-
-void CanvasState::rotate(float degrees) {
- mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
-}
-
-void CanvasState::scale(float sx, float sy) {
- mSnapshot->transform->scale(sx, sy, 1.0f);
-}
-
-void CanvasState::skew(float sx, float sy) {
- mSnapshot->transform->skew(sx, sy);
-}
-
-void CanvasState::setMatrix(const SkMatrix& matrix) {
- mSnapshot->transform->load(matrix);
-}
-
-void CanvasState::setMatrix(const Matrix4& matrix) {
- *(mSnapshot->transform) = matrix;
-}
-
-void CanvasState::concatMatrix(const SkMatrix& matrix) {
- mat4 transform(matrix);
- mSnapshot->transform->multiply(transform);
-}
-
-void CanvasState::concatMatrix(const Matrix4& matrix) {
- mSnapshot->transform->multiply(matrix);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clip
-///////////////////////////////////////////////////////////////////////////////
-
-bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
- mSnapshot->clip(Rect(left, top, right, bottom), op);
- return !mSnapshot->clipIsEmpty();
-}
-
-bool CanvasState::clipPath(const SkPath* path, SkClipOp op) {
- mSnapshot->clipPath(*path, op);
- return !mSnapshot->clipIsEmpty();
-}
-
-void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
- Rect bounds;
- float radius;
- if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported
-
- bool outlineIsRounded = MathUtils::isPositive(radius);
- if (!outlineIsRounded || currentTransform()->isSimple()) {
- // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
- clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
- }
- if (outlineIsRounded) {
- setClippingRoundRect(allocator, bounds, radius, false);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Quick Rejection
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
- * the clipRect. Does not modify the scissor.
- *
- * @param clipRequired if not null, will be set to true if element intersects clip
- * (and wasn't rejected)
- *
- * @param snapOut if set, the geometry will be treated as having an AA ramp.
- * See Rect::snapGeometryToPixelBoundaries()
- */
-bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom,
- bool* clipRequired, bool* roundRectClipRequired,
- bool snapOut) const {
- if (bottom <= top || right <= left) {
- return true;
- }
-
- Rect r(left, top, right, bottom);
- currentTransform()->mapRect(r);
- r.snapGeometryToPixelBoundaries(snapOut);
-
- Rect clipRect(currentRenderTargetClip());
- clipRect.snapToPixelBoundaries();
-
- if (!clipRect.intersects(r)) return true;
-
- // clip is required if geometry intersects clip rect
- if (clipRequired) {
- *clipRequired = !clipRect.contains(r);
- }
-
- // round rect clip is required if RR clip exists, and geometry intersects its corners
- if (roundRectClipRequired) {
- *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr &&
- mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
- }
- return false;
-}
-
-bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const {
- if (bottom <= top || right <= left) {
- return true;
- }
-
- Rect r(left, top, right, bottom);
- currentTransform()->mapRect(r);
- r.roundOut(); // rounded out to be conservative
-
- Rect clipRect(currentRenderTargetClip());
- clipRect.snapToPixelBoundaries();
-
- if (!clipRect.intersects(r)) return true;
-
- return false;
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
deleted file mode 100644
index 9ac35ff..0000000
--- a/libs/hwui/CanvasState.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Snapshot.h"
-
-#include <SkClipOp.h>
-#include <SkMatrix.h>
-#include <SkPath.h>
-#include <SkRegion.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Abstract base class for any class containing CanvasState.
- * Defines three mandatory callbacks.
- */
-class CanvasStateClient {
-public:
- CanvasStateClient() {}
- virtual ~CanvasStateClient() {}
-
- /**
- * Callback allowing embedder to take actions in the middle of a
- * setViewport() call.
- */
- virtual void onViewportInitialized() = 0;
-
- /**
- * Callback allowing embedder to take actions in the middle of a
- * restore() call. May be called several times sequentially.
- */
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0;
-
- /**
- * Allows subclasses to control what value is stored in snapshot's
- * fbo field in * initializeSaveStack.
- */
- virtual GLuint getTargetFbo() const = 0;
-
-}; // class CanvasStateClient
-
-/**
- * Implements Canvas state methods on behalf of Renderers.
- *
- * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the
- * Renderer interface. Drawing and recording classes that include a CanvasState will have
- * different use cases:
- *
- * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as
- * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating
- * the stack itself.
- *
- * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass
- * through state operations to CanvasState, so that not only will querying operations work
- * (getClip/Matrix), but so that quickRejection can also be used.
- */
-
-class CanvasState {
-public:
- explicit CanvasState(CanvasStateClient& renderer);
- ~CanvasState();
-
- /**
- * Initializes the first snapshot, computing the projection matrix,
- * and stores the dimensions of the render target.
- */
- void initializeRecordingSaveStack(int viewportWidth, int viewportHeight);
-
- /**
- * Initializes the first snapshot, computing the projection matrix,
- * and stores the dimensions of the render target.
- */
- void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop,
- float clipRight, float clipBottom, const Vector3& lightCenter);
-
- bool hasRectToRectTransform() const { return CC_LIKELY(currentTransform()->rectToRect()); }
-
- // Save (layer)
- int getSaveCount() const { return mSaveCount; }
- int save(int flags);
- void restore();
- void restoreToCount(int saveCount);
-
- // Save/Restore without side-effects
- int saveSnapshot(int flags);
- void restoreSnapshot();
-
- // Matrix
- void getMatrix(SkMatrix* outMatrix) const;
- void translate(float dx, float dy, float dz = 0.0f);
- void rotate(float degrees);
- void scale(float sx, float sy);
- void skew(float sx, float sy);
-
- void setMatrix(const SkMatrix& matrix);
- void setMatrix(const Matrix4& matrix); // internal only convenience method
- void concatMatrix(const SkMatrix& matrix);
- void concatMatrix(const Matrix4& matrix); // internal only convenience method
-
- // Clip
- const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
- const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
-
- bool quickRejectConservative(float left, float top, float right, float bottom) const;
-
- bool clipRect(float left, float top, float right, float bottom, SkClipOp op);
- bool clipPath(const SkPath* path, SkClipOp op);
-
- /**
- * Sets a "clipping outline", which is independent from the regular clip.
- * Currently only supports rectangles or rounded rectangles; passing in a
- * more complicated outline fails silently. Replaces any previous clipping
- * outline.
- */
- void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
- void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius,
- bool highPriority = true) {
- mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
- }
- void setProjectionPathMask(const SkPath* path) { mSnapshot->setProjectionPathMask(path); }
-
- /**
- * Returns true if drawing in the rectangle (left, top, right, bottom)
- * will be clipped out. Is conservative: might return false when subpixel-
- * perfect tests would return true.
- */
- bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
- bool* clipRequired, bool* roundRectClipRequired,
- bool snapOut) const;
-
- void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; }
-
- inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
- inline const Rect& currentRenderTargetClip() const {
- return currentSnapshot()->getRenderTargetClip();
- }
- inline int currentFlags() const { return currentSnapshot()->flags; }
- const Vector3& currentLightCenter() const {
- return currentSnapshot()->getRelativeLightCenter();
- }
- int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
- int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
- int getWidth() const { return mWidth; }
- int getHeight() const { return mHeight; }
- bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
-
- inline const Snapshot* currentSnapshot() const { return mSnapshot; }
- inline Snapshot* writableSnapshot() { return mSnapshot; }
- inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
-
-private:
- Snapshot* allocSnapshot(Snapshot* previous, int savecount);
- void freeSnapshot(Snapshot* snapshot);
- void freeAllSnapshots();
-
- /// Dimensions of the drawing surface
- int mWidth, mHeight;
-
- /// Number of saved states
- int mSaveCount;
-
- /// Base state
- Snapshot mFirstSnapshot;
-
- /// Host providing callbacks
- CanvasStateClient& mCanvas;
-
- /// Current state
- Snapshot* mSnapshot;
-
- // Pool of allocated snapshots to re-use
- // NOTE: The dtors have already been invoked!
- Snapshot* mSnapshotPool = nullptr;
- int mSnapshotPoolCount = 0;
-
-}; // class CanvasState
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
deleted file mode 100644
index 27d93cf..0000000
--- a/libs/hwui/ClipArea.cpp
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "ClipArea.h"
-
-#include "utils/LinearAllocator.h"
-
-#include <SkPath.h>
-#include <limits>
-#include <type_traits>
-
-namespace android {
-namespace uirenderer {
-
-static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
- Vertex v = {x, y};
- transform.mapPoint(v.x, v.y);
- transformedBounds.expandToCover(v.x, v.y);
-}
-
-Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
- const float kMinFloat = std::numeric_limits<float>::lowest();
- const float kMaxFloat = std::numeric_limits<float>::max();
- Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat};
- handlePoint(transformedBounds, transform, r.left, r.top);
- handlePoint(transformedBounds, transform, r.right, r.top);
- handlePoint(transformedBounds, transform, r.left, r.bottom);
- handlePoint(transformedBounds, transform, r.right, r.bottom);
- return transformedBounds;
-}
-
-void ClipBase::dump() const {
- ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect));
-}
-
-/*
- * TransformedRectangle
- */
-
-TransformedRectangle::TransformedRectangle() {}
-
-TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform)
- : mBounds(bounds), mTransform(transform) {}
-
-bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const {
- return mTransform == other.mTransform;
-}
-
-void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
- mBounds.doIntersect(other.mBounds);
-}
-
-bool TransformedRectangle::isEmpty() const {
- return mBounds.isEmpty();
-}
-
-/*
- * RectangleList
- */
-
-RectangleList::RectangleList() : mTransformedRectanglesCount(0) {}
-
-bool RectangleList::isEmpty() const {
- if (mTransformedRectanglesCount < 1) {
- return true;
- }
-
- for (int i = 0; i < mTransformedRectanglesCount; i++) {
- if (mTransformedRectangles[i].isEmpty()) {
- return true;
- }
- }
- return false;
-}
-
-int RectangleList::getTransformedRectanglesCount() const {
- return mTransformedRectanglesCount;
-}
-
-const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
- return mTransformedRectangles[i];
-}
-
-void RectangleList::setEmpty() {
- mTransformedRectanglesCount = 0;
-}
-
-void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
- mTransformedRectanglesCount = 1;
- mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
-}
-
-bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) {
- TransformedRectangle newRectangle(bounds, transform);
-
- // Try to find a rectangle with a compatible transformation
- int index = 0;
- for (; index < mTransformedRectanglesCount; index++) {
- TransformedRectangle& tr(mTransformedRectangles[index]);
- if (tr.canSimplyIntersectWith(newRectangle)) {
- tr.intersectWith(newRectangle);
- return true;
- }
- }
-
- // Add it to the list if there is room
- if (index < kMaxTransformedRectangles) {
- mTransformedRectangles[index] = newRectangle;
- mTransformedRectanglesCount += 1;
- return true;
- }
-
- // This rectangle list is full
- return false;
-}
-
-Rect RectangleList::calculateBounds() const {
- Rect bounds;
- for (int index = 0; index < mTransformedRectanglesCount; index++) {
- const TransformedRectangle& tr(mTransformedRectangles[index]);
- if (index == 0) {
- bounds = tr.transformedBounds();
- } else {
- bounds.doIntersect(tr.transformedBounds());
- }
- }
- return bounds;
-}
-
-static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) {
- SkPath rectPath;
- SkPath rectPathTransformed;
- rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
- SkMatrix skTransform;
- transform.copyTo(skTransform);
- rectPath.transform(skTransform, &rectPathTransformed);
- return rectPathTransformed;
-}
-
-SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
- SkRegion rectangleListAsRegion;
- for (int index = 0; index < mTransformedRectanglesCount; index++) {
- const TransformedRectangle& tr(mTransformedRectangles[index]);
- SkPath rectPathTransformed =
- pathFromTransformedRectangle(tr.getBounds(), tr.getTransform());
- if (index == 0) {
- rectangleListAsRegion.setPath(rectPathTransformed, clip);
- } else {
- SkRegion rectRegion;
- rectRegion.setPath(rectPathTransformed, clip);
- rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
- }
- }
- return rectangleListAsRegion;
-}
-
-void RectangleList::transform(const Matrix4& transform) {
- for (int index = 0; index < mTransformedRectanglesCount; index++) {
- mTransformedRectangles[index].transform(transform);
- }
-}
-
-/*
- * ClipArea
- */
-
-ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {}
-
-/*
- * Interface
- */
-
-void ClipArea::setViewportDimensions(int width, int height) {
- mPostViewportClipObserved = false;
- mViewportBounds.set(0, 0, width, height);
- mClipRect = mViewportBounds;
-}
-
-void ClipArea::setEmpty() {
- onClipUpdated();
- mMode = ClipMode::Rectangle;
- mClipRect.setEmpty();
- mClipRegion.setEmpty();
- mRectangleList.setEmpty();
-}
-
-void ClipArea::setClip(float left, float top, float right, float bottom) {
- onClipUpdated();
- mMode = ClipMode::Rectangle;
- mClipRect.set(left, top, right, bottom);
- mClipRegion.setEmpty();
-}
-
-void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
- if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
- onClipUpdated();
- switch (mMode) {
- case ClipMode::Rectangle:
- rectangleModeClipRectWithTransform(r, transform, op);
- break;
- case ClipMode::RectangleList:
- rectangleListModeClipRectWithTransform(r, transform, op);
- break;
- case ClipMode::Region:
- regionModeClipRectWithTransform(r, transform, op);
- break;
- }
-}
-
-void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
- if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
- onClipUpdated();
- enterRegionMode();
- mClipRegion.op(region, op);
- onClipRegionUpdated();
-}
-
-void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
- if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
- onClipUpdated();
- SkMatrix skTransform;
- transform->copyTo(skTransform);
- SkPath transformed;
- path.transform(skTransform, &transformed);
- SkRegion region;
- regionFromPath(transformed, region);
- enterRegionMode();
- mClipRegion.op(region, op);
- onClipRegionUpdated();
-}
-
-/*
- * Rectangle mode
- */
-
-void ClipArea::enterRectangleMode() {
- // Entering rectangle mode discards any
- // existing clipping information from the other modes.
- // The only way this occurs is by a clip setting operation.
- mMode = ClipMode::Rectangle;
-}
-
-void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
- mClipRect = r;
- transform->mapRect(mClipRect);
- return;
- } else if (op != SkRegion::kIntersect_Op) {
- enterRegionMode();
- regionModeClipRectWithTransform(r, transform, op);
- return;
- }
-
- if (transform->rectToRect()) {
- Rect transformed(r);
- transform->mapRect(transformed);
- mClipRect.doIntersect(transformed);
- return;
- }
-
- enterRectangleListMode();
- rectangleListModeClipRectWithTransform(r, transform, op);
-}
-
-/*
- * RectangleList mode implementation
- */
-
-void ClipArea::enterRectangleListMode() {
- // Is is only legal to enter rectangle list mode from
- // rectangle mode, since rectangle list mode cannot represent
- // all clip areas that can be represented by a region.
- ALOG_ASSERT(mMode == ClipMode::Rectangle);
- mMode = ClipMode::RectangleList;
- mRectangleList.set(mClipRect, Matrix4::identity());
-}
-
-void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op) {
- if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) {
- enterRegionMode();
- regionModeClipRectWithTransform(r, transform, op);
- }
-}
-
-/*
- * Region mode implementation
- */
-
-void ClipArea::enterRegionMode() {
- ClipMode oldMode = mMode;
- mMode = ClipMode::Region;
- if (oldMode != ClipMode::Region) {
- if (oldMode == ClipMode::Rectangle) {
- mClipRegion.setRect(mClipRect.toSkIRect());
- } else {
- mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
- onClipRegionUpdated();
- }
- }
-}
-
-void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op) {
- SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
- SkRegion transformedRectRegion;
- regionFromPath(transformedRect, transformedRectRegion);
- mClipRegion.op(transformedRectRegion, op);
- onClipRegionUpdated();
-}
-
-void ClipArea::onClipRegionUpdated() {
- if (!mClipRegion.isEmpty()) {
- mClipRect.set(mClipRegion.getBounds());
-
- if (mClipRegion.isRect()) {
- mClipRegion.setEmpty();
- enterRectangleMode();
- }
- } else {
- mClipRect.setEmpty();
- }
-}
-
-/**
- * Clip serialization
- */
-
-const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
- if (!mPostViewportClipObserved) {
- // Only initial clip-to-viewport observed, so no serialization of clip necessary
- return nullptr;
- }
-
- static_assert(std::is_trivially_destructible<Rect>::value,
- "expect Rect to be trivially destructible");
- static_assert(std::is_trivially_destructible<RectangleList>::value,
- "expect RectangleList to be trivially destructible");
-
- if (mLastSerialization == nullptr) {
- ClipBase* serialization = nullptr;
- switch (mMode) {
- case ClipMode::Rectangle:
- serialization = allocator.create<ClipRect>(mClipRect);
- break;
- case ClipMode::RectangleList:
- serialization = allocator.create<ClipRectList>(mRectangleList);
- serialization->rect = mRectangleList.calculateBounds();
- break;
- case ClipMode::Region:
- serialization = allocator.create<ClipRegion>(mClipRegion);
- serialization->rect.set(mClipRegion.getBounds());
- break;
- }
- serialization->intersectWithRoot = mReplaceOpObserved;
- // TODO: this is only done for draw time, should eventually avoid for record time
- serialization->rect.snapToPixelBoundaries();
- mLastSerialization = serialization;
- }
- return mLastSerialization;
-}
-
-inline static const RectangleList& getRectList(const ClipBase* scb) {
- return reinterpret_cast<const ClipRectList*>(scb)->rectList;
-}
-
-inline static const SkRegion& getRegion(const ClipBase* scb) {
- return reinterpret_cast<const ClipRegion*>(scb)->region;
-}
-
-// Conservative check for too many rectangles to fit in rectangle list.
-// For simplicity, doesn't account for rect merging
-static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
- int currentRectCount = clipArea.isRectangleList()
- ? clipArea.getRectangleList().getTransformedRectanglesCount()
- : 1;
- int recordedRectCount = (scb->mode == ClipMode::RectangleList)
- ? getRectList(scb).getTransformedRectanglesCount()
- : 1;
- return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
-}
-
-static const ClipRect sEmptyClipRect(Rect(0, 0));
-
-const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
- const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform) {
- // if no recordedClip passed, just serialize current state
- if (!recordedClip) return serializeClip(allocator);
-
- // if either is empty, clip is empty
- if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect;
-
- if (!mLastResolutionResult || recordedClip != mLastResolutionClip ||
- recordedClipTransform != mLastResolutionTransform) {
- mLastResolutionClip = recordedClip;
- mLastResolutionTransform = recordedClipTransform;
-
- if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle &&
- recordedClipTransform.rectToRect())) {
- // common case - result is a single rectangle
- auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
- recordedClipTransform.mapRect(rectClip->rect);
- rectClip->rect.doIntersect(mClipRect);
- rectClip->rect.snapToPixelBoundaries();
- mLastResolutionResult = rectClip;
- } else if (CC_UNLIKELY(mMode == ClipMode::Region ||
- recordedClip->mode == ClipMode::Region ||
- cannotFitInRectangleList(*this, recordedClip))) {
- // region case
- SkRegion other;
- switch (recordedClip->mode) {
- case ClipMode::Rectangle:
- if (CC_LIKELY(recordedClipTransform.rectToRect())) {
- // simple transform, skip creating SkPath
- Rect resultClip(recordedClip->rect);
- recordedClipTransform.mapRect(resultClip);
- other.setRect(resultClip.toSkIRect());
- } else {
- SkPath transformedRect = pathFromTransformedRectangle(
- recordedClip->rect, recordedClipTransform);
- other.setPath(transformedRect, createViewportRegion());
- }
- break;
- case ClipMode::RectangleList: {
- RectangleList transformedList(getRectList(recordedClip));
- transformedList.transform(recordedClipTransform);
- other = transformedList.convertToRegion(createViewportRegion());
- break;
- }
- case ClipMode::Region:
- other = getRegion(recordedClip);
- applyTransformToRegion(recordedClipTransform, &other);
- }
-
- ClipRegion* regionClip = allocator.create<ClipRegion>();
- switch (mMode) {
- case ClipMode::Rectangle:
- regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
- break;
- case ClipMode::RectangleList:
- regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
- other, SkRegion::kIntersect_Op);
- break;
- case ClipMode::Region:
- regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
- break;
- }
- // Don't need to snap, since region's in int bounds
- regionClip->rect.set(regionClip->region.getBounds());
- mLastResolutionResult = regionClip;
- } else {
- auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
- auto&& rectList = rectListClip->rectList;
- if (mMode == ClipMode::Rectangle) {
- rectList.set(mClipRect, Matrix4::identity());
- }
-
- if (recordedClip->mode == ClipMode::Rectangle) {
- rectList.intersectWith(recordedClip->rect, recordedClipTransform);
- } else {
- const RectangleList& other = getRectList(recordedClip);
- for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
- auto&& tr = other.getTransformedRectangle(i);
- Matrix4 totalTransform(recordedClipTransform);
- totalTransform.multiply(tr.getTransform());
- rectList.intersectWith(tr.getBounds(), totalTransform);
- }
- }
- rectListClip->rect = rectList.calculateBounds();
- rectListClip->rect.snapToPixelBoundaries();
- mLastResolutionResult = rectListClip;
- }
- }
- return mLastResolutionResult;
-}
-
-void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
- if (!clip) return; // nothing to do
-
- if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
- clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
- } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
- auto&& rectList = getRectList(clip);
- for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
- auto&& tr = rectList.getTransformedRectangle(i);
- Matrix4 totalTransform(transform);
- totalTransform.multiply(tr.getTransform());
- clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
- }
- } else {
- SkRegion region(getRegion(clip));
- applyTransformToRegion(transform, ®ion);
- clipRegion(region, SkRegion::kIntersect_Op);
- }
-}
-
-void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) {
- if (transform.rectToRect() && !transform.isPureTranslate()) {
- // handle matrices with scale manually by mapping each rect
- SkRegion other;
- SkRegion::Iterator it(*region);
- while (!it.done()) {
- Rect rect(it.rect());
- transform.mapRect(rect);
- rect.snapGeometryToPixelBoundaries(true);
- other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op);
- it.next();
- }
- region->swap(other);
- } else {
- // TODO: handle non-translate transforms properly!
- region->translate(transform.getTranslateX(), transform.getTranslateY());
- }
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
deleted file mode 100644
index a7a1180..0000000
--- a/libs/hwui/ClipArea.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef CLIPAREA_H
-#define CLIPAREA_H
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "utils/Pair.h"
-
-#include <SkRegion.h>
-
-namespace android {
-namespace uirenderer {
-
-class LinearAllocator;
-
-Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
-
-class TransformedRectangle {
-public:
- TransformedRectangle();
- TransformedRectangle(const Rect& bounds, const Matrix4& transform);
-
- bool canSimplyIntersectWith(const TransformedRectangle& other) const;
- void intersectWith(const TransformedRectangle& other);
-
- bool isEmpty() const;
-
- const Rect& getBounds() const { return mBounds; }
-
- Rect transformedBounds() const {
- Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
- return transformedBounds;
- }
-
- const Matrix4& getTransform() const { return mTransform; }
-
- void transform(const Matrix4& transform) {
- Matrix4 t;
- t.loadMultiply(transform, mTransform);
- mTransform = t;
- }
-
-private:
- Rect mBounds;
- Matrix4 mTransform;
-};
-
-class RectangleList {
-public:
- RectangleList();
-
- bool isEmpty() const;
- int getTransformedRectanglesCount() const;
- const TransformedRectangle& getTransformedRectangle(int i) const;
-
- void setEmpty();
- void set(const Rect& bounds, const Matrix4& transform);
- bool intersectWith(const Rect& bounds, const Matrix4& transform);
- void transform(const Matrix4& transform);
-
- SkRegion convertToRegion(const SkRegion& clip) const;
- Rect calculateBounds() const;
-
- enum { kMaxTransformedRectangles = 5 };
-
-private:
- int mTransformedRectanglesCount;
- TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
-};
-
-enum class ClipMode {
- Rectangle,
- RectangleList,
-
- // region and path - intersected. if either is empty, don't use
- Region
-};
-
-struct ClipBase {
- explicit ClipBase(ClipMode mode) : mode(mode) {}
- explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {}
- const ClipMode mode;
- bool intersectWithRoot = false;
- // Bounds of the clipping area, used to define the scissor, and define which
- // portion of the stencil is updated/used
- Rect rect;
-
- void dump() const;
-};
-
-struct ClipRect : ClipBase {
- explicit ClipRect(const Rect& rect) : ClipBase(rect) {}
-};
-
-struct ClipRectList : ClipBase {
- explicit ClipRectList(const RectangleList& rectList)
- : ClipBase(ClipMode::RectangleList), rectList(rectList) {}
- RectangleList rectList;
-};
-
-struct ClipRegion : ClipBase {
- explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {}
- ClipRegion() : ClipBase(ClipMode::Region) {}
- SkRegion region;
-};
-
-class ClipArea {
-public:
- ClipArea();
-
- void setViewportDimensions(int width, int height);
-
- bool isEmpty() const { return mClipRect.isEmpty(); }
-
- void setEmpty();
- void setClip(float left, float top, float right, float bottom);
- void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
- void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op);
-
- const Rect& getClipRect() const { return mClipRect; }
-
- const SkRegion& getClipRegion() const { return mClipRegion; }
-
- const RectangleList& getRectangleList() const { return mRectangleList; }
-
- bool isRegion() const { return ClipMode::Region == mMode; }
-
- bool isSimple() const { return mMode == ClipMode::Rectangle; }
-
- bool isRectangleList() const { return mMode == ClipMode::RectangleList; }
-
- WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
- WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
- LinearAllocator& allocator, const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform);
- void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
-
- static void applyTransformToRegion(const Matrix4& transform, SkRegion* region);
-
-private:
- void enterRectangleMode();
- void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-
- void enterRectangleListMode();
- void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op);
-
- void enterRegionModeFromRectangleMode();
- void enterRegionModeFromRectangleListMode();
- void enterRegionMode();
- void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-
- void clipRegion(const SkRegion& region, SkRegion::Op op);
- void ensureClipRegion();
- void onClipRegionUpdated();
-
- // Called by every state modifying public method.
- void onClipUpdated() {
- mPostViewportClipObserved = true;
- mLastSerialization = nullptr;
- mLastResolutionResult = nullptr;
- }
-
- SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); }
-
- void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
- // TODO: this should not mask every path to the viewport - this makes it impossible to use
- // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
- pathAsRegion.setPath(path, createViewportRegion());
- }
-
- ClipMode mMode;
- bool mPostViewportClipObserved = false;
- bool mReplaceOpObserved = false;
-
- /**
- * If mLastSerialization is non-null, it represents an already serialized copy
- * of the current clip state. If null, it has not been computed.
- */
- const ClipBase* mLastSerialization = nullptr;
-
- /**
- * This pair of pointers is a single entry cache of most recently seen
- */
- const ClipBase* mLastResolutionResult = nullptr;
- const ClipBase* mLastResolutionClip = nullptr;
- Matrix4 mLastResolutionTransform;
-
- Rect mViewportBounds;
- Rect mClipRect;
- SkRegion mClipRegion;
- RectangleList mRectangleList;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* CLIPAREA_H_ */
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
deleted file mode 100644
index b424f97..0000000
--- a/libs/hwui/FloatColor.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef FLOATCOLOR_H
-#define FLOATCOLOR_H
-
-#include "utils/Color.h"
-#include "utils/Macros.h"
-#include "utils/MathUtils.h"
-
-#include <stdint.h>
-
-namespace android {
-namespace uirenderer {
-
-struct FloatColor {
- // "color" is a gamma-encoded sRGB color
- // After calling this method, the color is stored as a pre-multiplied linear color
- // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied
- // gamma-encoded sRGB color
- void set(uint32_t color) {
- a = ((color >> 24) & 0xff) / 255.0f;
- r = a * EOCF(((color >> 16) & 0xff) / 255.0f);
- g = a * EOCF(((color >> 8) & 0xff) / 255.0f);
- b = a * EOCF(((color)&0xff) / 255.0f);
- }
-
- // "color" is a gamma-encoded sRGB color
- // After calling this method, the color is stored as a un-premultiplied linear color
- // if linear blending is enabled. Otherwise, the color is stored as a un-premultiplied
- // gamma-encoded sRGB color
- void setUnPreMultiplied(uint32_t color) {
- a = ((color >> 24) & 0xff) / 255.0f;
- r = EOCF(((color >> 16) & 0xff) / 255.0f);
- g = EOCF(((color >> 8) & 0xff) / 255.0f);
- b = EOCF(((color)&0xff) / 255.0f);
- }
-
- bool isNotBlack() { return a < 1.0f || r > 0.0f || g > 0.0f || b > 0.0f; }
-
- bool operator==(const FloatColor& other) const {
- return MathUtils::areEqual(r, other.r) && MathUtils::areEqual(g, other.g) &&
- MathUtils::areEqual(b, other.b) && MathUtils::areEqual(a, other.a);
- }
-
- bool operator!=(const FloatColor& other) const { return !(*this == other); }
-
- float r;
- float g;
- float b;
- float a;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FloatColor);
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* FLOATCOLOR_H */
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
deleted file mode 100644
index 65bee47..0000000
--- a/libs/hwui/ResourceCache.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ResourceCache.h"
-
-namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
-
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Resource cache
-///////////////////////////////////////////////////////////////////////////////
-
-void ResourceCache::logCache() {
- ALOGD("ResourceCache: cacheReport:");
- for (size_t i = 0; i < mCache->size(); ++i) {
- ResourceReference* ref = mCache->valueAt(i);
- ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i),
- mCache->valueAt(i));
- ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", i,
- ref->refCount, ref->destroyed, ref->resourceType);
- }
-}
-
-ResourceCache::ResourceCache() {
- Mutex::Autolock _l(mLock);
- mCache = new KeyedVector<const void*, ResourceReference*>();
-}
-
-ResourceCache::~ResourceCache() {
- Mutex::Autolock _l(mLock);
- delete mCache;
-}
-
-void ResourceCache::lock() {
- mLock.lock();
-}
-
-void ResourceCache::unlock() {
- mLock.unlock();
-}
-
-void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
- Mutex::Autolock _l(mLock);
- incrementRefcountLocked(resource, resourceType);
-}
-
-void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
- incrementRefcount((void*)patchResource, kNinePatch);
-}
-
-void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
- if (ref == nullptr || mCache->size() == 0) {
- ref = new ResourceReference(resourceType);
- mCache->add(resource, ref);
- }
- ref->refCount++;
-}
-
-void ResourceCache::decrementRefcount(void* resource) {
- Mutex::Autolock _l(mLock);
- decrementRefcountLocked(resource);
-}
-
-void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
- decrementRefcount((void*)patchResource);
-}
-
-void ResourceCache::decrementRefcountLocked(void* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
- if (ref == nullptr) {
- // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
- return;
- }
- ref->refCount--;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
-void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
- decrementRefcountLocked((void*)patchResource);
-}
-
-void ResourceCache::destructor(Res_png_9patch* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(Res_png_9patch* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
- if (ref == nullptr) {
- // If we're not tracking this resource, just delete it
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- delete[](int8_t*) resource;
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
-/**
- * This method should only be called while the mLock mutex is held (that mutex is grabbed
- * by the various destructor() and recycle() methods which call this method).
- */
-void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
- if (ref->destroyed) {
- switch (ref->resourceType) {
- case kNinePatch: {
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- int8_t* patch = (int8_t*)resource;
- delete[] patch;
- } break;
- }
- }
- mCache->removeItem(resource);
- delete ref;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
deleted file mode 100644
index fd3f9fd..0000000
--- a/libs/hwui/ResourceCache.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_RESOURCE_CACHE_H
-#define ANDROID_HWUI_RESOURCE_CACHE_H
-
-#include <cutils/compiler.h>
-
-#include <SkBitmap.h>
-#include <SkPixelRef.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/Singleton.h>
-
-#include <androidfw/ResourceTypes.h>
-
-namespace android {
-namespace uirenderer {
-
-class Layer;
-
-/**
- * Type of Resource being cached
- */
-enum ResourceType {
- kNinePatch,
-};
-
-class ResourceReference {
-public:
- explicit ResourceReference(ResourceType type) {
- refCount = 0;
- destroyed = false;
- resourceType = type;
- }
-
- int refCount;
- bool destroyed;
- ResourceType resourceType;
-};
-
-class ANDROID_API ResourceCache : public Singleton<ResourceCache> {
- ResourceCache();
- ~ResourceCache();
-
- friend class Singleton<ResourceCache>;
-
-public:
- /**
- * When using these two methods, make sure to only invoke the *Locked()
- * variants of increment/decrementRefcount(), recyle() and destructor()
- */
- void lock();
- void unlock();
-
- void incrementRefcount(const Res_png_9patch* resource);
-
- void decrementRefcount(const Res_png_9patch* resource);
-
- void decrementRefcountLocked(const Res_png_9patch* resource);
-
- void destructor(Res_png_9patch* resource);
-
- void destructorLocked(Res_png_9patch* resource);
-
-private:
- void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref);
-
- void incrementRefcount(void* resource, ResourceType resourceType);
- void incrementRefcountLocked(void* resource, ResourceType resourceType);
-
- void decrementRefcount(void* resource);
- void decrementRefcountLocked(void* resource);
-
- void logCache();
-
- /**
- * Used to increment, decrement, and destroy. Incrementing is generally accessed on the UI
- * thread, but destroying resources may be called from the GC thread, the finalizer thread,
- * or a reference queue finalization thread.
- */
- mutable Mutex mLock;
-
- KeyedVector<const void*, ResourceReference*>* mCache;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_RESOURCE_CACHE_H
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
deleted file mode 100644
index f1a1bef..0000000
--- a/libs/hwui/Snapshot.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Snapshot.h"
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors
-///////////////////////////////////////////////////////////////////////////////
-
-Snapshot::Snapshot()
- : flags(0)
- , previous(nullptr)
- , layer(nullptr)
- , fbo(0)
- , alpha(1.0f)
- , roundRectClipState(nullptr)
- , projectionPathMask(nullptr)
- , mClipArea(&mClipAreaRoot) {
- transform = &mTransformRoot;
- mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0;
-}
-
-/**
- * Copies the specified snapshot/ The specified snapshot is stored as
- * the previous snapshot.
- */
-Snapshot::Snapshot(Snapshot* s, int saveFlags)
- : flags(0)
- , previous(s)
- , layer(s->layer)
- , fbo(s->fbo)
- , alpha(s->alpha)
- , roundRectClipState(s->roundRectClipState)
- , projectionPathMask(s->projectionPathMask)
- , mClipArea(nullptr)
- , mViewportData(s->mViewportData)
- , mRelativeLightCenter(s->mRelativeLightCenter) {
- if (saveFlags & SaveFlags::Matrix) {
- mTransformRoot = *s->transform;
- transform = &mTransformRoot;
- } else {
- transform = s->transform;
- }
-
- if (saveFlags & SaveFlags::Clip) {
- mClipAreaRoot = s->getClipArea();
- mClipArea = &mClipAreaRoot;
- } else {
- mClipArea = s->mClipArea;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::clip(const Rect& localClip, SkClipOp op) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op));
-}
-
-void Snapshot::clipPath(const SkPath& path, SkClipOp op) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op));
-}
-
-void Snapshot::setClip(float left, float top, float right, float bottom) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->setClip(left, top, right, bottom);
-}
-
-bool Snapshot::hasPerspectiveTransform() const {
- return transform->isPerspective();
-}
-
-const Rect& Snapshot::getLocalClip() {
- mat4 inverse;
- inverse.loadInverse(*transform);
-
- mLocalClip.set(mClipArea->getClipRect());
- inverse.mapRect(mLocalClip);
-
- return mLocalClip;
-}
-
-void Snapshot::resetClip(float left, float top, float right, float bottom) {
- // TODO: This is incorrect, when we start rendering into a new layer,
- // we may have to modify the previous snapshot's clip rect and clip
- // region if the previous restore() call did not restore the clip
- mClipArea = &mClipAreaRoot;
- setClip(left, top, right, bottom);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping round rect
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius,
- bool highPriority) {
- if (bounds.isEmpty()) {
- mClipArea->setEmpty();
- return;
- }
-
- if (roundRectClipState && roundRectClipState->highPriority) {
- // ignore, don't replace, already have a high priority clip
- return;
- }
-
- RoundRectClipState* state = new (allocator) RoundRectClipState;
-
- state->highPriority = highPriority;
-
- // store the inverse drawing matrix
- Matrix4 roundRectDrawingMatrix = getOrthoMatrix();
- roundRectDrawingMatrix.multiply(*transform);
- state->matrix.loadInverse(roundRectDrawingMatrix);
-
- // compute area under rounded corners - only draws overlapping these rects need to be clipped
- for (int i = 0; i < 4; i++) {
- state->dangerRects[i] = bounds;
- }
- state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
- state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
- state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
- state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
- for (int i = 0; i < 4; i++) {
- transform->mapRect(state->dangerRects[i]);
-
- // round danger rects out as though they are AA geometry (since they essentially are)
- state->dangerRects[i].snapGeometryToPixelBoundaries(true);
- }
-
- // store RR area
- state->innerRect = bounds;
- state->innerRect.inset(radius);
- state->radius = radius;
-
- // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
- roundRectClipState = state;
-}
-
-void Snapshot::setProjectionPathMask(const SkPath* path) {
- projectionPathMask = path;
-}
-
-static Snapshot* getClipRoot(Snapshot* target) {
- while (target->previous && target->previous->previous) {
- target = target->previous;
- }
- return target;
-}
-
-const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator,
- const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform) {
- auto target = this;
- if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
- // Clip must be intersected with root, instead of current clip.
- target = getClipRoot(this);
- }
-
- return target->mClipArea->serializeIntersectedClip(allocator, recordedClip,
- recordedClipTransform);
-}
-
-void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) {
- if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
- // current clip is being replaced, but must intersect with clip root
- *mClipArea = *(getClipRoot(this)->mClipArea);
- }
- mClipArea->applyClip(recordedClip, transform);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Queries
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::dump() const {
- ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous,
- getViewportHeight(), !mClipArea->isSimple());
- const Rect& clipRect(mClipArea->getClipRect());
- ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top,
- clipRect.right, clipRect.bottom, mClipArea->isSimple());
-
- ALOGD(" Transform (at %p):", transform);
- transform->dump();
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
deleted file mode 100644
index 655f819..0000000
--- a/libs/hwui/Snapshot.h
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/Region.h>
-#include <utils/LinearAllocator.h>
-#include <utils/RefBase.h>
-
-#include <SkClipOp.h>
-#include <SkRegion.h>
-
-#include "ClipArea.h"
-#include "Layer.h"
-#include "Matrix.h"
-#include "Outline.h"
-#include "Rect.h"
-#include "utils/Macros.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Temporary structure holding information for a single outline clip.
- *
- * These structures are treated as immutable once created, and only exist for a single frame, which
- * is why they may only be allocated with a LinearAllocator.
- */
-class RoundRectClipState {
-public:
- static void* operator new(size_t size) = delete;
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc<RoundRectClipState>(size);
- }
-
- bool areaRequiresRoundRectClip(const Rect& rect) const {
- return rect.intersects(dangerRects[0]) || rect.intersects(dangerRects[1]) ||
- rect.intersects(dangerRects[2]) || rect.intersects(dangerRects[3]);
- }
-
- bool highPriority;
- Matrix4 matrix;
- Rect dangerRects[4];
- Rect innerRect;
- float radius;
-};
-
-/**
- * A snapshot holds information about the current state of the rendering
- * surface. A snapshot is usually created whenever the user calls save()
- * and discarded when the user calls restore(). Once a snapshot is created,
- * it can hold information for deferred rendering.
- *
- * Each snapshot has a link to a previous snapshot, indicating the previous
- * state of the renderer.
- */
-class Snapshot {
-public:
- Snapshot();
- Snapshot(Snapshot* s, int saveFlags);
-
- /**
- * Various flags set on ::flags.
- */
- enum Flags {
- /**
- * Indicates that the clip region was modified. When this
- * snapshot is restored so must the clip.
- */
- kFlagClipSet = 0x1,
- /**
- * Indicates that this snapshot was created when saving
- * a new layer.
- */
- kFlagIsLayer = 0x2,
- /**
- * Indicates that this snapshot is a special type of layer
- * backed by an FBO. This flag only makes sense when the
- * flag kFlagIsLayer is also set.
- *
- * Viewport has been modified to fit the new Fbo, and must be
- * restored when this snapshot is restored.
- */
- kFlagIsFboLayer = 0x4,
- };
-
- /**
- * Modifies the current clip with the new clip rectangle and
- * the specified operation. The specified rectangle is transformed
- * by this snapshot's trasnformation.
- */
- void clip(const Rect& localClip, SkClipOp op);
-
- /**
- * Modifies the current clip with the new clip rectangle and
- * the specified operation. The specified rectangle is considered
- * already transformed.
- */
- void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect);
-
- /**
- * Modifies the current clip with the specified path and operation.
- */
- void clipPath(const SkPath& path, SkClipOp op);
-
- /**
- * Sets the current clip.
- */
- void setClip(float left, float top, float right, float bottom);
-
- /**
- * Returns the current clip in local coordinates. The clip rect is
- * transformed by the inverse transform matrix.
- */
- ANDROID_API const Rect& getLocalClip();
-
- /**
- * Returns the current clip in render target coordinates.
- */
- const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); }
-
- /*
- * Accessor functions so that the clip area can stay private
- */
- bool clipIsEmpty() const { return mClipArea->isEmpty(); }
- const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
- bool clipIsSimple() const { return mClipArea->isSimple(); }
- const ClipArea& getClipArea() const { return *mClipArea; }
- ClipArea& mutateClipArea() { return *mClipArea; }
-
- WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
- LinearAllocator& allocator, const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform);
- void applyClip(const ClipBase* clip, const Matrix4& transform);
-
- /**
- * Resets the clip to the specified rect.
- */
- void resetClip(float left, float top, float right, float bottom);
-
- void initializeViewport(int width, int height) {
- mViewportData.initialize(width, height);
- mClipAreaRoot.setViewportDimensions(width, height);
- }
-
- int getViewportWidth() const { return mViewportData.mWidth; }
- int getViewportHeight() const { return mViewportData.mHeight; }
- const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
-
- const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; }
- void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; }
-
- /**
- * Sets (and replaces) the current clipping outline
- *
- * If the current round rect clip is high priority, the incoming clip is ignored.
- */
- void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius,
- bool highPriority);
-
- /**
- * Sets (and replaces) the current projection mask
- */
- void setProjectionPathMask(const SkPath* path);
-
- /**
- * Indicates whether the current transform has perspective components.
- */
- bool hasPerspectiveTransform() const;
-
- /**
- * Dirty flags.
- */
- int flags;
-
- /**
- * Previous snapshot.
- */
- Snapshot* previous;
-
- /**
- * A pointer to the currently active layer.
- *
- * This snapshot does not own the layer, this pointer must not be freed.
- */
- Layer* layer;
-
- /**
- * Target FBO used for rendering. Set to 0 when rendering directly
- * into the framebuffer.
- */
- GLuint fbo;
-
- /**
- * Local transformation. Holds the current translation, scale and
- * rotation values.
- *
- * This is a reference to a matrix owned by this snapshot or another
- * snapshot. This pointer must not be freed. See ::mTransformRoot.
- */
- mat4* transform;
-
- /**
- * Current alpha value. This value is 1 by default, but may be set by a DisplayList which
- * has translucent rendering in a non-overlapping View. This value will be used by
- * the renderer to set the alpha in the current color being used for ensuing drawing
- * operations. The value is inherited by child snapshots because the same value should
- * be applied to descendants of the current DisplayList (for example, a TextView contains
- * the base alpha value which should be applied to the child DisplayLists used for drawing
- * the actual text).
- */
- float alpha;
-
- /**
- * Current clipping round rect.
- *
- * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips,
- * never modified.
- */
- const RoundRectClipState* roundRectClipState;
-
- /**
- * Current projection masking path - used exclusively to mask projected, tessellated circles.
- */
- const SkPath* projectionPathMask;
-
- void dump() const;
-
-private:
- struct ViewportData {
- ViewportData() : mWidth(0), mHeight(0) {}
- void initialize(int width, int height) {
- mWidth = width;
- mHeight = height;
- mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
- }
-
- /*
- * Width and height of current viewport.
- *
- * The viewport is always defined to be (0, 0, width, height).
- */
- int mWidth;
- int mHeight;
- /**
- * Contains the current orthographic, projection matrix.
- */
- mat4 mOrthoMatrix;
- };
-
- mat4 mTransformRoot;
-
- ClipArea mClipAreaRoot;
- ClipArea* mClipArea;
- Rect mLocalClip;
-
- ViewportData mViewportData;
- Vector3 mRelativeLightCenter;
-
-}; // class Snapshot
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index a6aae55..f091277 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -19,7 +19,6 @@
#include "Vector.h"
-#include "FloatColor.h"
#include "utils/Macros.h"
namespace android {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b04194f..753557c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -224,6 +224,7 @@
break;
case PixelStorageType::Heap:
free(mPixelStorage.heap.address);
+ mallopt(M_PURGE, 0);
break;
case PixelStorageType::Hardware:
auto buffer = mPixelStorage.hardware.buffer;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 0022c93..c6e4c154b 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -141,7 +141,7 @@
// select only flags that might affect text layout
flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
- SkPaint::kAutoHinting_Flag | SkPaint::kVerticalText_Flag);
+ SkPaint::kAutoHinting_Flag);
flags |= (hinting << 16);
return flags;
}
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index a00b8db..c5db861d 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -21,7 +21,6 @@
#include <Properties.h>
#include <Rect.h>
#include <RenderNode.h>
-#include <Snapshot.h>
#include <hwui/Bitmap.h>
#include <pipeline/skia/SkiaRecordingCanvas.h>
#include <private/hwui/DrawGlInfo.h>
@@ -141,14 +140,6 @@
return true;
}
- static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
- std::unique_ptr<Snapshot> snapshot(new Snapshot());
- // store clip first, so it isn't transformed
- snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom);
- *(snapshot->transform) = transform;
- return snapshot;
- }
-
static sk_sp<Bitmap> createBitmap(int width, int height,
SkColorType colorType = kN32_SkColorType) {
SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index e3c97ce..524dfb0 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -288,7 +288,7 @@
case '?':
fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
- // fall-through
+ [[fallthrough]];
default:
error = true;
break;
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 9388c20..70423a7 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -16,7 +16,6 @@
#include <benchmark/benchmark.h>
-#include "CanvasState.h"
#include "DisplayList.h"
#include "hwui/Canvas.h"
#include "pipeline/skia/SkiaDisplayList.h"
@@ -116,52 +115,6 @@
}
BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView);
-class NullClient : public CanvasStateClient {
- void onViewportInitialized() override {}
- void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
- GLuint getTargetFbo() const override { return 0; }
-};
-
-void BM_CanvasState_saverestore(benchmark::State& benchState) {
- NullClient client;
- CanvasState state(client);
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
- while (benchState.KeepRunning()) {
- state.save(SaveFlags::MatrixClip);
- state.save(SaveFlags::MatrixClip);
- benchmark::DoNotOptimize(&state);
- state.restore();
- state.restore();
- }
-}
-BENCHMARK(BM_CanvasState_saverestore);
-
-void BM_CanvasState_init(benchmark::State& benchState) {
- NullClient client;
- CanvasState state(client);
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
- while (benchState.KeepRunning()) {
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
- benchmark::DoNotOptimize(&state);
- }
-}
-BENCHMARK(BM_CanvasState_init);
-
-void BM_CanvasState_translate(benchmark::State& benchState) {
- NullClient client;
- CanvasState state(client);
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
- while (benchState.KeepRunning()) {
- state.translate(5, 5, 0);
- benchmark::DoNotOptimize(&state);
- state.translate(-5, -5, 0);
- }
-}
-BENCHMARK(BM_CanvasState_translate);
-
void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) {
sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) {
canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
deleted file mode 100644
index 4c03811..0000000
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CanvasState.h"
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "hwui/Canvas.h"
-#include "utils/LinearAllocator.h"
-
-#include <SkClipOp.h>
-#include <SkPath.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace uirenderer {
-
-class NullClient : public CanvasStateClient {
- void onViewportInitialized() override {}
- void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
- GLuint getTargetFbo() const override { return 0; }
-};
-
-static NullClient sNullClient;
-
-static bool approxEqual(const Matrix4& a, const Matrix4& b) {
- for (int i = 0; i < 16; i++) {
- if (!MathUtils::areEqual(a[i], b[i])) {
- return false;
- }
- }
- return true;
-}
-
-TEST(CanvasState, gettersAndSetters) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- ASSERT_EQ(state.getWidth(), 200);
- ASSERT_EQ(state.getHeight(), 200);
-
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(10, 20, 0);
- state.setMatrix(simpleTranslate);
-
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200));
- ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180));
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
- EXPECT_TRUE(state.clipIsSimple());
-}
-
-TEST(CanvasState, simpleClipping) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
-
- state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
-
- state.clipRect(50, 50, 150, 150, SkClipOp::kReplace_deprecated);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
-}
-
-TEST(CanvasState, complexClipping) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.save(SaveFlags::MatrixClip);
- {
- // rotated clip causes complex clip
- state.rotate(10);
- EXPECT_TRUE(state.clipIsSimple());
- state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
- EXPECT_FALSE(state.clipIsSimple());
- }
- state.restore();
-
- state.save(SaveFlags::MatrixClip);
- {
- // subtracted clip causes complex clip
- EXPECT_TRUE(state.clipIsSimple());
- state.clipRect(50, 50, 150, 150, SkClipOp::kDifference);
- EXPECT_FALSE(state.clipIsSimple());
- }
- state.restore();
-
- state.save(SaveFlags::MatrixClip);
- {
- // complex path causes complex clip
- SkPath path;
- path.addOval(SkRect::MakeWH(200, 200));
- EXPECT_TRUE(state.clipIsSimple());
- state.clipPath(&path, SkClipOp::kDifference);
- EXPECT_FALSE(state.clipIsSimple());
- }
- state.restore();
-}
-
-TEST(CanvasState, saveAndRestore) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.save(SaveFlags::Clip);
- {
- state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
- }
- state.restore();
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore
-
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(10, 10, 0);
- state.save(SaveFlags::Matrix);
- {
- state.translate(10, 10, 0);
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
- }
- state.restore();
- EXPECT_FALSE(approxEqual(*state.currentTransform(), simpleTranslate));
-}
-
-TEST(CanvasState, saveAndRestoreButNotTooMuch) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.save(SaveFlags::Matrix); // NOTE: clip not saved
- {
- state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
- }
- state.restore();
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored
-
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(10, 10, 0);
- state.save(SaveFlags::Clip); // NOTE: matrix not saved
- {
- state.translate(10, 10, 0);
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
- }
- state.restore();
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored
-}
-}
-}
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
deleted file mode 100644
index 450bb67..0000000
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkPath.h>
-#include <SkRegion.h>
-#include <gtest/gtest.h>
-
-#include "ClipArea.h"
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "utils/LinearAllocator.h"
-
-namespace android {
-namespace uirenderer {
-
-static Rect kViewportBounds(2048, 2048);
-
-static ClipArea createClipArea() {
- ClipArea area;
- area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight());
- return area;
-}
-
-TEST(TransformedRectangle, basics) {
- Rect r(0, 0, 100, 100);
- Matrix4 minus90;
- minus90.loadRotate(-90);
- minus90.mapRect(r);
- Rect r2(20, 40, 120, 60);
-
- Matrix4 m90;
- m90.loadRotate(90);
- TransformedRectangle tr(r, m90);
- EXPECT_TRUE(tr.canSimplyIntersectWith(tr));
-
- Matrix4 m0;
- TransformedRectangle tr0(r2, m0);
- EXPECT_FALSE(tr.canSimplyIntersectWith(tr0));
-
- Matrix4 m45;
- m45.loadRotate(45);
- TransformedRectangle tr2(r, m45);
- EXPECT_FALSE(tr2.canSimplyIntersectWith(tr));
-}
-
-TEST(RectangleList, basics) {
- RectangleList list;
- EXPECT_TRUE(list.isEmpty());
-
- Rect r(0, 0, 100, 100);
- Matrix4 m45;
- m45.loadRotate(45);
- list.set(r, m45);
- EXPECT_FALSE(list.isEmpty());
-
- Rect r2(20, 20, 200, 200);
- list.intersectWith(r2, m45);
- EXPECT_FALSE(list.isEmpty());
- EXPECT_EQ(1, list.getTransformedRectanglesCount());
-
- Rect r3(20, 20, 200, 200);
- Matrix4 m30;
- m30.loadRotate(30);
- list.intersectWith(r2, m30);
- EXPECT_FALSE(list.isEmpty());
- EXPECT_EQ(2, list.getTransformedRectanglesCount());
-
- SkRegion clip;
- clip.setRect(0, 0, 2000, 2000);
- SkRegion rgn(list.convertToRegion(clip));
- EXPECT_FALSE(rgn.isEmpty());
-}
-
-TEST(ClipArea, basics) {
- ClipArea area(createClipArea());
- EXPECT_FALSE(area.isEmpty());
-}
-
-TEST(ClipArea, paths) {
- ClipArea area(createClipArea());
- SkPath path;
- SkScalar r = 100;
- path.addCircle(r, r, r);
- area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op);
- EXPECT_FALSE(area.isEmpty());
- EXPECT_FALSE(area.isSimple());
- EXPECT_FALSE(area.isRectangleList());
-
- Rect clipRect(area.getClipRect());
- Rect expected(0, 0, r * 2, r * 2);
- EXPECT_EQ(expected, clipRect);
- SkRegion clipRegion(area.getClipRegion());
- auto skRect(clipRegion.getBounds());
- Rect regionBounds;
- regionBounds.set(skRect);
- EXPECT_EQ(expected, regionBounds);
-}
-
-TEST(ClipArea, replaceNegative) {
- ClipArea area(createClipArea());
- area.setClip(0, 0, 100, 100);
-
- Rect expected(-50, -50, 50, 50);
- area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op);
- EXPECT_EQ(expected, area.getClipRect());
-}
-
-TEST(ClipArea, serializeClip) {
- ClipArea area(createClipArea());
- LinearAllocator allocator;
-
- // unset clip
- EXPECT_EQ(nullptr, area.serializeClip(allocator));
-
- // rect clip
- area.setClip(0, 0, 200, 200);
- {
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode);
- ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
- EXPECT_EQ(Rect(200, 200), serializedClip->rect);
- EXPECT_EQ(serializedClip, area.serializeClip(allocator))
- << "Requery of clip on unmodified ClipArea must return same pointer.";
- }
-
- // rect list
- Matrix4 rotate;
- rotate.loadRotate(5.0f);
- area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op);
- {
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
- ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
- auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
- EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
- EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect);
- EXPECT_EQ(serializedClip, area.serializeClip(allocator))
- << "Requery of clip on unmodified ClipArea must return same pointer.";
- }
-
- // region
- SkPath circlePath;
- circlePath.addCircle(100, 100, 100);
- area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
- {
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- ASSERT_EQ(ClipMode::Region, serializedClip->mode);
- ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot";
- auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip);
- EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds())
- << "Clip region should be 200x200";
- EXPECT_EQ(Rect(200, 200), clipRegion->rect);
- EXPECT_EQ(serializedClip, area.serializeClip(allocator))
- << "Requery of clip on unmodified ClipArea must return same pointer.";
- }
-}
-
-TEST(ClipArea, serializeClip_pathIntersectWithRoot) {
- ClipArea area(createClipArea());
- LinearAllocator allocator;
- SkPath circlePath;
- circlePath.addCircle(100, 100, 100);
- area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op);
-
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
-}
-
-TEST(ClipArea, serializeIntersectedClip) {
- ClipArea area(createClipArea());
- LinearAllocator allocator;
-
- // simple state;
- EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
- area.setClip(0, 0, 200, 200);
- {
- auto origRectClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, origRectClip);
- EXPECT_EQ(origRectClip,
- area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
- }
-
- // rect
- {
- ClipRect recordedClip(Rect(100, 100));
- Matrix4 translateScale;
- translateScale.loadTranslate(100, 100, 0);
- translateScale.scale(2, 3, 1);
- auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
- ASSERT_NE(nullptr, resolvedClip);
- ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode);
- EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect);
-
- EXPECT_EQ(resolvedClip,
- area.serializeIntersectedClip(allocator, &recordedClip, translateScale))
- << "Must return previous serialization, since input is same";
-
- ClipRect recordedClip2(Rect(100, 100));
- EXPECT_NE(resolvedClip,
- area.serializeIntersectedClip(allocator, &recordedClip2, translateScale))
- << "Shouldn't return previous serialization, since matrix location is different";
- }
-
- // rect list
- Matrix4 rotate;
- rotate.loadRotate(2.0f);
- area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
- {
- ClipRect recordedClip(Rect(100, 100));
- auto resolvedClip =
- area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity());
- ASSERT_NE(nullptr, resolvedClip);
- ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode);
- auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip);
- EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
- }
-
- // region
- SkPath circlePath;
- circlePath.addCircle(100, 100, 100);
- area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
- {
- SkPath ovalPath;
- ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200));
-
- ClipRegion recordedClip;
- recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200)));
- recordedClip.rect = Rect(200, 200);
-
- Matrix4 translate10x20;
- translate10x20.loadTranslate(10, 20, 0);
- auto resolvedClip = area.serializeIntersectedClip(
- allocator, &recordedClip,
- translate10x20); // Note: only translate for now, others not handled correctly
- ASSERT_NE(nullptr, resolvedClip);
- ASSERT_EQ(ClipMode::Region, resolvedClip->mode);
- auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
- EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds());
- }
-}
-
-TEST(ClipArea, serializeIntersectedClip_snap) {
- ClipArea area(createClipArea());
- area.setClip(100.2, 100.4, 500.6, 500.8);
- LinearAllocator allocator;
-
- {
- // no recorded clip case
- auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity());
- EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect);
- }
- {
- // recorded clip case
- ClipRect recordedClip(Rect(100.12, 100.74));
- Matrix4 translateScale;
- translateScale.loadTranslate(100, 100, 0);
- translateScale.scale(2, 3,
- 1); // recorded clip will have non-int coords, even after transform
- auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
- ASSERT_NE(nullptr, resolvedClip);
- EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode);
- EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect);
- }
-}
-
-TEST(ClipArea, serializeIntersectedClip_scale) {
- ClipArea area(createClipArea());
- area.setClip(0, 0, 400, 400);
- LinearAllocator allocator;
-
- SkPath circlePath;
- circlePath.addCircle(50, 50, 50);
-
- ClipRegion recordedClip;
- recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100)));
- recordedClip.rect = Rect(100, 100);
-
- Matrix4 translateScale;
- translateScale.loadTranslate(100, 100, 0);
- translateScale.scale(2, 2, 1);
- auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
-
- ASSERT_NE(nullptr, resolvedClip);
- EXPECT_EQ(ClipMode::Region, resolvedClip->mode);
- EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect);
- auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
- EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_identity) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- ClipArea::applyTransformToRegion(Matrix4::identity(), ®ion);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_translate) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.loadTranslate(10, 20, 0);
- ClipArea::applyTransformToRegion(transform, ®ion);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_scale) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.loadScale(2, 3, 1);
- ClipArea::applyTransformToRegion(transform, ®ion);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_translateScale) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.translate(10, 20);
- transform.scale(2, 3, 1);
- ClipArea::applyTransformToRegion(transform, ®ion);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_rotate90) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.loadRotate(90);
- ClipArea::applyTransformToRegion(transform, ®ion);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds());
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2926ef3..2c73940 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -32,6 +32,7 @@
#include "pipeline/skia/SkiaRecordingCanvas.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
+#include "utils/Color.h"
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index bc742b0..df5f456 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -35,23 +35,6 @@
return bitmap;
}
-/**
- * 1x1 bitmaps must not be optimized into solid color shaders, since HWUI can't
- * compose/render color shaders
- */
-TEST(SkiaBehavior, CreateBitmapShader1x1) {
- SkBitmap origBitmap = createSkBitmap(1, 1);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(origBitmap, kNever_SkCopyPixelsMode);
- sk_sp<SkShader> s =
- image->makeShader(SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, nullptr);
-
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- ASSERT_TRUE(s->isABitmap(&bitmap, nullptr, xy))
- << "1x1 bitmap shader must query as bitmap shader";
- EXPECT_EQ(origBitmap.pixelRef(), bitmap.pixelRef());
-}
-
TEST(SkiaBehavior, genIds) {
SkBitmap bitmap = createSkBitmap(100, 100);
uint32_t genId = bitmap.getGenerationID();
diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp
deleted file mode 100644
index 9d673c8..0000000
--- a/libs/hwui/tests/unit/SnapshotTests.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <Snapshot.h>
-
-#include <tests/common/TestUtils.h>
-
-using namespace android::uirenderer;
-
-TEST(Snapshot, serializeIntersectedClip) {
- auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
- auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
- auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
- root->previous = actualRoot.get();
- child->previous = root.get();
-
- LinearAllocator allocator;
- ClipRect rect(Rect(0, 0, 75, 75));
- {
- auto intersectWithChild =
- child->serializeIntersectedClip(allocator, &rect, Matrix4::identity());
- ASSERT_NE(nullptr, intersectWithChild);
- EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child";
- }
-
- rect.intersectWithRoot = true;
- {
- auto intersectWithRoot =
- child->serializeIntersectedClip(allocator, &rect, Matrix4::identity());
- ASSERT_NE(nullptr, intersectWithRoot);
- EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root";
- }
-}
-
-TEST(Snapshot, applyClip) {
- auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
- auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
- root->previous = actualRoot.get();
-
- ClipRect rect(Rect(0, 0, 75, 75));
- {
- auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
- child->previous = root.get();
- child->applyClip(&rect, Matrix4::identity());
-
- EXPECT_TRUE(child->getClipArea().isSimple());
- EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip());
- }
-
- {
- rect.intersectWithRoot = true;
- auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
- child->previous = root.get();
- child->applyClip(&rect, Matrix4::identity());
-
- EXPECT_TRUE(child->getClipArea().isSimple());
- EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip());
- }
-}
diff --git a/media/OWNERS b/media/OWNERS
index 1ae2a7b..0abf9ae 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -9,4 +9,4 @@
wjia@google.com
jaewan@google.com
chz@google.com
-
+dwkang@google.com
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c074cce..e8c97bc 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3942,18 +3942,31 @@
}
/**
- * Indicate Hearing Aid connection state change.
+ * Indicate Hearing Aid connection state change and eventually suppress
+ * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
* @param device Bluetooth device connected/disconnected
* @param state new connection state (BluetoothProfile.STATE_xxx)
+ * @param musicDevice Default get system volume for the connecting device.
+ * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or
+ * {@link android.bluetooth.BluetoothProfile.HEARING_AID})
+ * @param suppressNoisyIntent if true the
+ * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+ * @return a delay in ms that the caller should wait before broadcasting
+ * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent.
* {@hide}
*/
- public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) {
+ public int setBluetoothHearingAidDeviceConnectionState(
+ BluetoothDevice device, int state, boolean suppressNoisyIntent,
+ int musicDevice) {
final IAudioService service = getService();
+ int delay = 0;
try {
- service.setHearingAidDeviceConnectionState(device, state);
+ delay = service.setBluetoothHearingAidDeviceConnectionState(device,
+ state, suppressNoisyIntent, musicDevice);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ return delay;
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 569db16..abd6411 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -151,8 +151,6 @@
void setWiredDeviceConnectionState(int type, int state, String address, String name,
String caller);
- void setHearingAidDeviceConnectionState(in BluetoothDevice device, int state);
-
int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
@@ -210,6 +208,9 @@
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
+ int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
+ int state, boolean suppressNoisyIntent, int musicDevice);
+
int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6d122d7..ee12b91 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1192,15 +1192,17 @@
public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
/**
- * The maximum security level supported by the device. This is the default
- * security level when a session is opened.
+ * Indicates that the maximum security level supported by the device should
+ * be used when opening a session. This is the default security level
+ * selected when a session is opened.
* @hide
*/
public static final int SECURITY_LEVEL_MAX = 6;
/**
- * The maximum security level supported by the device. This is the default
- * security level when a session is opened.
+ * Returns a value that may be passed as a parameter to {@link #openSession(int)}
+ * requesting that the session be opened at the maximum security level of
+ * the device.
*/
public static final int getMaxSecurityLevel() {
return SECURITY_LEVEL_MAX;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index db6da8c..e94413c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -37,7 +37,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -1025,50 +1024,6 @@
public abstract MediaTimestamp getTimestamp();
/**
- * Gets the media metadata.
- *
- * @param update_only controls whether the full set of available
- * metadata is returned or just the set that changed since the
- * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
- * #METADATA_ALL}.
- *
- * @param apply_filter if true only metadata that matches the
- * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
- * #BYPASS_METADATA_FILTER}.
- *
- * @return The metadata, possibly empty. null if an error occured.
- // FIXME: unhide.
- * {@hide}
- */
- public Metadata getMetadata(final boolean update_only,
- final boolean apply_filter) {
- return null;
- }
-
- /**
- * Set a filter for the metadata update notification and update
- * retrieval. The caller provides 2 set of metadata keys, allowed
- * and blocked. The blocked set always takes precedence over the
- * allowed one.
- * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
- * shorthands to allow/block all or no metadata.
- *
- * By default, there is no filter set.
- *
- * @param allow Is the set of metadata the client is interested
- * in receiving new notifications for.
- * @param block Is the set of metadata the client is not interested
- * in receiving new notifications for.
- * @return The call status code.
- *
- // FIXME: unhide.
- * {@hide}
- */
- public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
- return 0;
- }
-
- /**
* Resets the MediaPlayer2 to its uninitialized state. After calling
* this method, you will have to initialize it again by setting the
* data source and calling prepare().
@@ -2266,38 +2221,4 @@
public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
}
-
- /**
- Constant to retrieve only the new metadata since the last
- call.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean METADATA_UPDATE_ONLY = true;
-
- /**
- Constant to retrieve all the metadata.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean METADATA_ALL = false;
-
- /**
- Constant to enable the metadata filter during retrieval.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean APPLY_METADATA_FILTER = true;
-
- /**
- Constant to disable the metadata filter during retrieval.
- // FIXME: unhide.
- // FIXME: add link to getMetadata(boolean, boolean)
- {@hide}
- */
- public static final boolean BYPASS_METADATA_FILTER = false;
-
}
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 5604ffd..71df7dc 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -25,7 +25,6 @@
import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
import android.graphics.Rect;
-import android.media.MediaPlayer2Proto;
import android.media.MediaPlayer2Proto.PlayerMessage;
import android.media.MediaPlayer2Proto.Value;
import android.net.Uri;
@@ -461,12 +460,12 @@
@Override
void process() {
mVolume = volume;
- _setVolume(volume, volume);
+ _setVolume(volume);
}
});
}
- private native void _setVolume(float leftVolume, float rightVolume);
+ private native void _setVolume(float volume);
/**
* Returns the current volume of this player to this player.
@@ -683,7 +682,9 @@
case DataSourceDesc.TYPE_CALLBACK:
handleDataSource(isCurrent,
srcId,
- dsd.getMedia2DataSource());
+ dsd.getMedia2DataSource(),
+ dsd.getStartPosition(),
+ dsd.getEndPosition());
break;
case DataSourceDesc.TYPE_FD:
@@ -691,7 +692,9 @@
srcId,
dsd.getFileDescriptor(),
dsd.getFileDescriptorOffset(),
- dsd.getFileDescriptorLength());
+ dsd.getFileDescriptorLength(),
+ dsd.getStartPosition(),
+ dsd.getEndPosition());
break;
case DataSourceDesc.TYPE_URI:
@@ -700,7 +703,9 @@
dsd.getUriContext(),
dsd.getUri(),
dsd.getUriHeaders(),
- dsd.getUriCookies());
+ dsd.getUriCookies(),
+ dsd.getStartPosition(),
+ dsd.getEndPosition());
break;
default:
@@ -730,67 +735,77 @@
private void handleDataSource(
boolean isCurrent, long srcId,
@NonNull Context context, @NonNull Uri uri,
- @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
+ @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies,
+ long startPos, long endPos)
throws IOException {
- // The context and URI usually belong to the calling user. Get a resolver for that user
- // and strip out the userId from the URI if present.
+ // The context and URI usually belong to the calling user. Get a resolver for that user.
final ContentResolver resolver = context.getContentResolver();
final String scheme = uri.getScheme();
- final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
- handleDataSource(isCurrent, srcId, uri.getPath(), null, null);
+ handleDataSource(isCurrent, srcId, uri.getPath(), null, null, startPos, endPos);
return;
}
- if (ContentResolver.SCHEME_CONTENT.equals(scheme)
- && Settings.AUTHORITY.equals(authority)) {
- // Try cached ringtone first since the actual provider may not be
- // encryption aware, or it may be stored on CE media storage
- final int type = RingtoneManager.getDefaultType(uri);
- final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
- final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
- if (attemptDataSource(isCurrent, srcId, resolver, cacheUri)) {
+ final int ringToneType = RingtoneManager.getDefaultType(uri);
+ try {
+ AssetFileDescriptor afd;
+ // Try requested Uri locally first
+ if (ContentResolver.SCHEME_CONTENT.equals(scheme) && ringToneType != -1) {
+ afd = RingtoneManager.openDefaultRingtoneUri(context, uri);
+ if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) {
+ return;
+ }
+ final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(
+ context, ringToneType);
+ afd = resolver.openAssetFileDescriptor(actualUri, "r");
+ } else {
+ afd = resolver.openAssetFileDescriptor(uri, "r");
+ }
+ if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) {
return;
}
- if (attemptDataSource(isCurrent, srcId, resolver, actualUri)) {
- return;
- }
- handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
- } else {
- // Try requested Uri locally first, or fallback to media server
- if (attemptDataSource(isCurrent, srcId, resolver, uri)) {
- return;
- }
- handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
+ } catch (NullPointerException | SecurityException | IOException ex) {
+ Log.w(TAG, "Couldn't open " + uri + ": " + ex);
+ // Fallback to media server
}
+ handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies, startPos, endPos);
}
- private boolean attemptDataSource(
- boolean isCurrent, long srcId, ContentResolver resolver, Uri uri) {
- try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
+ private boolean attemptDataSource(boolean isCurrent, long srcId, AssetFileDescriptor afd,
+ long startPos, long endPos) throws IOException {
+ try {
if (afd.getDeclaredLength() < 0) {
handleDataSource(isCurrent,
- srcId,
- afd.getFileDescriptor(),
- 0,
- DataSourceDesc.LONG_MAX);
+ srcId,
+ afd.getFileDescriptor(),
+ 0,
+ DataSourceDesc.LONG_MAX,
+ startPos,
+ endPos);
} else {
handleDataSource(isCurrent,
- srcId,
- afd.getFileDescriptor(),
- afd.getStartOffset(),
- afd.getDeclaredLength());
+ srcId,
+ afd.getFileDescriptor(),
+ afd.getStartOffset(),
+ afd.getDeclaredLength(),
+ startPos,
+ endPos);
}
return true;
} catch (NullPointerException | SecurityException | IOException ex) {
- Log.w(TAG, "Couldn't open " + uri + ": " + ex);
+ Log.w(TAG, "Couldn't open srcId:" + srcId + ": " + ex);
return false;
+ } finally {
+ if (afd != null) {
+ afd.close();
+ }
}
}
private void handleDataSource(
boolean isCurrent, long srcId,
- String path, Map<String, String> headers, List<HttpCookie> cookies)
+ String path, Map<String, String> headers, List<HttpCookie> cookies,
+ long startPos, long endPos)
throws IOException {
String[] keys = null;
String[] values = null;
@@ -806,11 +821,12 @@
++i;
}
}
- handleDataSource(isCurrent, srcId, path, keys, values, cookies);
+ handleDataSource(isCurrent, srcId, path, keys, values, cookies, startPos, endPos);
}
private void handleDataSource(boolean isCurrent, long srcId,
- String path, String[] keys, String[] values, List<HttpCookie> cookies)
+ String path, String[] keys, String[] values, List<HttpCookie> cookies,
+ long startPos, long endPos)
throws IOException {
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
@@ -824,7 +840,9 @@
Media2HTTPService.createHTTPService(path, cookies),
path,
keys,
- values);
+ values,
+ startPos,
+ endPos);
return;
}
@@ -832,7 +850,7 @@
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
- handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX);
+ handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
is.close();
} else {
throw new IOException("handleDataSource failed.");
@@ -841,7 +859,8 @@
private native void nativeHandleDataSourceUrl(
boolean isCurrent, long srcId,
- Media2HTTPService httpService, String path, String[] keys, String[] values)
+ Media2HTTPService httpService, String path, String[] keys, String[] values,
+ long startPos, long endPos)
throws IOException;
/**
@@ -855,23 +874,27 @@
*/
private void handleDataSource(
boolean isCurrent, long srcId,
- FileDescriptor fd, long offset, long length) throws IOException {
- nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length);
+ FileDescriptor fd, long offset, long length,
+ long startPos, long endPos) throws IOException {
+ nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
}
private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
- FileDescriptor fd, long offset, long length) throws IOException;
+ FileDescriptor fd, long offset, long length,
+ long startPos, long endPos) throws IOException;
/**
* @throws IllegalStateException if it is called in an invalid state
* @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
*/
- private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource) {
- nativeHandleDataSourceCallback(isCurrent, srcId, dataSource);
+ private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource,
+ long startPos, long endPos) {
+ nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
}
private native void nativeHandleDataSourceCallback(
- boolean isCurrent, long srcId, Media2DataSource dataSource);
+ boolean isCurrent, long srcId, Media2DataSource dataSource,
+ long startPos, long endPos);
/**
* @return true if there is a next data source, false otherwise.
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 66feb1d..8664aa1 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
@@ -1128,6 +1129,38 @@
}
/**
+ * Opens a raw file descriptor to read the data under the given default URI.
+ *
+ * @param context the Context to use when resolving the Uri.
+ * @param uri The desired default URI to open.
+ * @return a new AssetFileDescriptor pointing to the file. You own this descriptor
+ * and are responsible for closing it when done. This value may be {@code null}.
+ * @throws FileNotFoundException if the provided URI could not be opened.
+ * @see #getDefaultUri
+ */
+ public static AssetFileDescriptor openDefaultRingtoneUri(
+ @NonNull Context context, @NonNull Uri uri) throws FileNotFoundException {
+ // Try cached ringtone first since the actual provider may not be
+ // encryption aware, or it may be stored on CE media storage
+ final int type = getDefaultType(uri);
+ final Uri cacheUri = getCacheForType(type, context.getUserId());
+ final Uri actualUri = getActualDefaultRingtoneUri(context, type);
+ final ContentResolver resolver = context.getContentResolver();
+
+ AssetFileDescriptor afd = null;
+ if (cacheUri != null) {
+ afd = resolver.openAssetFileDescriptor(cacheUri, "r");
+ if (afd != null) {
+ return afd;
+ }
+ }
+ if (actualUri != null) {
+ afd = resolver.openAssetFileDescriptor(actualUri, "r");
+ }
+ return afd;
+ }
+
+ /**
* Creates a {@link android.media.MediaScannerConnection} to scan a ringtone file and add its
* information to the internal database.
*
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
index a7eb30d..0c1d1a2 100644
--- a/media/java/android/media/update/ApiLoader.java
+++ b/media/java/android/media/update/ApiLoader.java
@@ -16,14 +16,64 @@
package android.media.update;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.GuardedBy;
+
+import dalvik.system.PathClassLoader;
+
+import java.io.File;
+
/**
* @hide
*/
public final class ApiLoader {
+ @GuardedBy("this")
+ private static StaticProvider sMediaUpdatable;
+
+ private static final String UPDATE_PACKAGE = "com.android.media.update";
+ private static final String UPDATE_CLASS = "com.android.media.update.ApiFactory";
+ private static final String UPDATE_METHOD = "initialize";
+ private static final boolean REGISTER_UPDATE_DEPENDENCY = true;
+
private ApiLoader() { }
public static StaticProvider getProvider() {
- throw new RuntimeException("Use MediaSession/Browser instead of"
- + " hidden MediaSession2/Browser2 APIs.");
+ try {
+ return getMediaUpdatable();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (NameNotFoundException | ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode.
+ private static synchronized StaticProvider getMediaUpdatable()
+ throws NameNotFoundException, ReflectiveOperationException, RemoteException {
+ if (sMediaUpdatable != null) return sMediaUpdatable;
+
+ // TODO Figure out when to use which package (query media update service)
+ int flags = Build.IS_DEBUGGABLE ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
+ ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ UPDATE_PACKAGE, flags, UserHandle.myUserId());
+
+ if (REGISTER_UPDATE_DEPENDENCY) {
+ // Register a dependency to the updatable in order to be killed during updates
+ ActivityManager.getService().addPackageDependency(ai.packageName);
+ }
+
+ ClassLoader classLoader = new PathClassLoader(ai.sourceDir,
+ ai.nativeLibraryDir + File.pathSeparator + System.getProperty("java.library.path"),
+ ClassLoader.getSystemClassLoader().getParent());
+ return sMediaUpdatable = (StaticProvider) classLoader.loadClass(UPDATE_CLASS)
+ .getMethod(UPDATE_METHOD, ApplicationInfo.class).invoke(null, ai);
}
}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index e67b100..474b671 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -538,8 +538,13 @@
MtpPropertyGroup propertyGroup;
for (MtpStorageManager.MtpObject obj : objs) {
if (property == 0xffffffff) {
+ if (format == 0 && handle != 0 && handle != 0xffffffff) {
+ // return properties based on the object's format
+ format = obj.getFormat();
+ }
// Get all properties supported by this object
- propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat());
+ // format should be the same between get & put
+ propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
int[] propertyList = getSupportedObjectProperties(format);
propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 3870124..fa9ab1f7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -128,6 +128,7 @@
"libmediametrics",
"libmediaplayer2",
"libmediaplayer2-protos",
+ "libmediandk_utils",
"libmediautils",
"libnetd_client", // for setNetworkForUser
"libprotobuf-cpp-lite",
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index b52da36..61c28ed 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -283,7 +283,8 @@
static void
android_media_MediaPlayer2_handleDataSourceUrl(
JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
- jobject httpServiceObj, jstring path, jobjectArray keys, jobjectArray values) {
+ jobject httpServiceObj, jstring path, jobjectArray keys, jobjectArray values,
+ jlong startPos, jlong endPos) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
@@ -300,7 +301,8 @@
if (tmp == NULL) { // Out of memory
return;
}
- ALOGV("handleDataSourceUrl: path %s, srcId %lld", tmp, (long long)srcId);
+ ALOGV("handleDataSourceUrl: path %s, srcId %lld, start %lld, end %lld",
+ tmp, (long long)srcId, (long long)startPos, (long long)endPos);
if (strncmp(tmp, "content://", 10) == 0) {
ALOGE("handleDataSourceUrl: content scheme is not supported in native code");
@@ -313,6 +315,8 @@
dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_URL;
dsd->mUrl = tmp;
+ dsd->mStartPositionMs = startPos;
+ dsd->mEndPositionMs = endPos;
env->ReleaseStringUTFChars(path, tmp);
tmp = NULL;
@@ -341,9 +345,9 @@
static void
android_media_MediaPlayer2_handleDataSourceFD(
- JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
- jobject fileDescriptor, jlong offset, jlong length)
-{
+ JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
+ jobject fileDescriptor, jlong offset, jlong length,
+ jlong startPos, jlong endPos) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -355,8 +359,10 @@
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ALOGV("handleDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld",
- (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
+ ALOGV("handleDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld, "
+ "start=%lld, end=%lld",
+ (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length,
+ (long long)startPos, (long long)endPos);
struct stat sb;
int ret = fstat(fd, &sb);
@@ -389,6 +395,8 @@
dsd->mFD = fd;
dsd->mFDOffset = offset;
dsd->mFDLength = length;
+ dsd->mStartPositionMs = startPos;
+ dsd->mEndPositionMs = endPos;
status_t err;
if (isCurrent) {
@@ -402,7 +410,8 @@
static void
android_media_MediaPlayer2_handleDataSourceCallback(
- JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId, jobject dataSource)
+ JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId, jobject dataSource,
+ jlong startPos, jlong endPos)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -419,6 +428,8 @@
dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_CALLBACK;
dsd->mCallbackSource = callbackDataSource;
+ dsd->mStartPositionMs = startPos;
+ dsd->mEndPositionMs = endPos;
status_t err;
if (isCurrent) {
@@ -974,15 +985,15 @@
}
static void
-android_media_MediaPlayer2_setVolume(JNIEnv *env, jobject thiz, jfloat leftVolume, jfloat rightVolume)
+android_media_MediaPlayer2_setVolume(JNIEnv *env, jobject thiz, jfloat volume)
{
- ALOGV("setVolume: left %f right %f", (float) leftVolume, (float) rightVolume);
+ ALOGV("setVolume: volume %f", (float) volume);
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
- process_media_player_call( env, thiz, mp->setVolume((float) leftVolume, (float) rightVolume), NULL, NULL );
+ process_media_player_call( env, thiz, mp->setVolume((float) volume), NULL, NULL );
}
static jbyteArray
@@ -1442,17 +1453,17 @@
{
"nativeHandleDataSourceUrl",
"(ZJLandroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
- "[Ljava/lang/String;)V",
+ "[Ljava/lang/String;JJ)V",
(void *)android_media_MediaPlayer2_handleDataSourceUrl
},
{
"nativeHandleDataSourceFD",
- "(ZJLjava/io/FileDescriptor;JJ)V",
+ "(ZJLjava/io/FileDescriptor;JJJJ)V",
(void *)android_media_MediaPlayer2_handleDataSourceFD
},
{
"nativeHandleDataSourceCallback",
- "(ZJLandroid/media/Media2DataSource;)V",
+ "(ZJLandroid/media/Media2DataSource;JJ)V",
(void *)android_media_MediaPlayer2_handleDataSourceCallback
},
{"nativePlayNextDataSource", "(J)V", (void *)android_media_MediaPlayer2_playNextDataSource},
@@ -1481,7 +1492,7 @@
{"getParameter", "(I)Ljava/lang/Object;", (void *)android_media_MediaPlayer2_getParameter},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
- {"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume},
+ {"_setVolume", "(F)V", (void *)android_media_MediaPlayer2_setVolume},
{"_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke},
{"native_init", "()V", (void *)android_media_MediaPlayer2_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer2_native_setup},
diff --git a/opengl/java/android/opengl/EGL15.java b/opengl/java/android/opengl/EGL15.java
new file mode 100644
index 0000000..9aae6ad
--- /dev/null
+++ b/opengl/java/android/opengl/EGL15.java
@@ -0,0 +1,149 @@
+/*
+** 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.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public class EGL15 {
+
+ public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 0x00000001;
+ public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 0x00000002;
+ public static final int EGL_OPENGL_ES3_BIT = 0x00000040;
+ public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 0x0001;
+ public static final int EGL_GL_COLORSPACE_SRGB = 0x3089;
+ public static final int EGL_GL_COLORSPACE_LINEAR = 0x308A;
+ public static final int EGL_CONTEXT_MAJOR_VERSION = 0x3098;
+ public static final int EGL_CL_EVENT_HANDLE = 0x309C;
+ public static final int EGL_GL_COLORSPACE = 0x309D;
+ public static final int EGL_GL_TEXTURE_2D = 0x30B1;
+ public static final int EGL_GL_TEXTURE_3D = 0x30B2;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x30B3;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x30B4;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x30B5;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x30B6;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x30B7;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x30B8;
+ public static final int EGL_GL_RENDERBUFFER = 0x30B9;
+ public static final int EGL_GL_TEXTURE_LEVEL = 0x30BC;
+ public static final int EGL_GL_TEXTURE_ZOFFSET = 0x30BD;
+ public static final int EGL_IMAGE_PRESERVED = 0x30D2;
+ public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 0x30F0;
+ public static final int EGL_SYNC_STATUS = 0x30F1;
+ public static final int EGL_SIGNALED = 0x30F2;
+ public static final int EGL_UNSIGNALED = 0x30F3;
+ public static final int EGL_TIMEOUT_EXPIRED = 0x30F5;
+ public static final int EGL_CONDITION_SATISFIED = 0x30F6;
+ public static final int EGL_SYNC_TYPE = 0x30F7;
+ public static final int EGL_SYNC_CONDITION = 0x30F8;
+ public static final int EGL_SYNC_FENCE = 0x30F9;
+ public static final int EGL_CONTEXT_MINOR_VERSION = 0x30FB;
+ public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 0x30FD;
+ public static final int EGL_SYNC_CL_EVENT = 0x30FE;
+ public static final int EGL_SYNC_CL_EVENT_COMPLETE = 0x30FF;
+ public static final int EGL_CONTEXT_OPENGL_DEBUG = 0x31B0;
+ public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 0x31B1;
+ public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 0x31B2;
+ public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+ public static final int EGL_NO_RESET_NOTIFICATION = 0x31BE;
+ public static final int EGL_LOSE_CONTEXT_ON_RESET = 0x31BF;
+ public static final int EGL_PLATFORM_ANDROID_KHR = 0x3141;
+ public static final long EGL_FOREVER = 0xFFFFFFFFFFFFFFFFL;
+ public static final EGLImage EGL_NO_IMAGE = null;
+ public static final EGLSync EGL_NO_SYNC = null;
+ public static final EGLContext EGL_NO_CONTEXT = null;
+ public static final EGLDisplay EGL_NO_DISPLAY = null;
+ public static final EGLSurface EGL_NO_SURFACE = null;
+
+ native private static void _nativeClassInit();
+ static {
+ _nativeClassInit();
+ }
+ // C function EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+
+ public static native EGLSync eglCreateSync(
+ EGLDisplay dpy,
+ int type,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+
+ public static native boolean eglDestroySync(
+ EGLDisplay dpy,
+ EGLSync sync
+ );
+
+ // C function EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+
+ public static native int eglClientWaitSync(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int flags,
+ long timeout
+ );
+
+ // C function EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+
+ public static native boolean eglGetSyncAttrib(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int attribute,
+ long[] value,
+ int offset
+ );
+
+ // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+ public static native EGLDisplay eglGetPlatformDisplay(
+ int platform,
+ long native_display,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+
+ public static native EGLSurface eglCreatePlatformWindowSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ java.nio.Buffer native_window,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+
+ public static native EGLSurface eglCreatePlatformPixmapSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ java.nio.Buffer native_pixmap,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+
+ public static native boolean eglWaitSync(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int flags
+ );
+
+}
diff --git a/opengl/java/android/opengl/EGLImage.java b/opengl/java/android/opengl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/java/android/opengl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+ private EGLImage(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLImage)) return false;
+
+ EGLImage that = (EGLImage) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/opengl/java/android/opengl/EGLSync.java b/opengl/java/android/opengl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/java/android/opengl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+ private EGLSync(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLSync)) return false;
+
+ EGLSync that = (EGLSync) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 33cb5964..4518d79 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -30,7 +30,7 @@
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
-import com.android.carrierdefaultapp.R;
+
/**
* This util class provides common logic for carrier actions
*/
@@ -102,7 +102,7 @@
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onDisableAllMeteredApns subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
- telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, !ENABLE);
+ telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(!ENABLE);
}
private static void onEnableAllMeteredApns(Intent intent, Context context) {
@@ -110,7 +110,7 @@
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onEnableAllMeteredApns subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
- telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, ENABLE);
+ telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(ENABLE);
}
private static void onEnableDefaultURLHandler(Context context) {
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index f9dbcd4..5d84d64 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -104,6 +104,6 @@
assertNotNull(pendingIntent);
Rlog.d(TAG, "verify carrier action: disable all metered apns");
- verify(mTelephonyMgr).carrierActionSetMeteredApnsEnabled(eq(subId), eq(false));
+ verify(mTelephonyMgr).setCarrierDataEnabled(eq(false));
}
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java b/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
index 5782ea1..31c9224 100644
--- a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
@@ -66,7 +66,7 @@
public void onNotificationSeen(NotificationEntry entry) {
// user has strong opinions about this notification. we can't down rank it, so don't bother.
- if (entry.getChannel().isImportanceLocked()) {
+ if (entry.getChannel().hasUserSetImportance()) {
return;
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 3333e15..8f33a70 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -25,7 +25,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.app.AlarmManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -224,8 +223,9 @@
}
/** A convenience helper for creating an adjustment for an SBN. */
+ @VisibleForTesting
@Nullable
- private Adjustment createEnqueuedNotificationAdjustment(
+ Adjustment createEnqueuedNotificationAdjustment(
@NonNull NotificationEntry entry,
@NonNull ArrayList<Notification.Action> smartActions,
@NonNull ArrayList<CharSequence> smartReplies) {
@@ -237,7 +237,9 @@
signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
}
if (mNotificationCategorizer.shouldSilence(entry)) {
- signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
+ final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance()
+ : IMPORTANCE_LOW;
+ signals.putInt(KEY_IMPORTANCE, importance);
}
return new Adjustment(
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 2820232..2eb005a 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -66,6 +66,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
+import java.util.ArrayList;
public class AssistantTest extends ServiceTestCase<Assistant> {
@@ -466,4 +467,12 @@
assertFalse(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
}
+
+ @Test
+ public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
+ Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry(
+ mPackageManager, sbn, P1C3), new ArrayList<>(), new ArrayList<>());
+ assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
+ }
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 8fed367..f2de9ec 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -54,9 +54,14 @@
Intent intent = getIntent();
String callingPackage = getCallingPackage();
+ final boolean isSessionInstall =
+ PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
+
// If the activity was started via a PackageInstaller session, we retrieve the calling
// package from that session
- int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
+ final int sessionId = (isSessionInstall
+ ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
+ : -1);
if (callingPackage == null && sessionId != -1) {
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
@@ -99,7 +104,7 @@
nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
- if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
+ if (isSessionInstall) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Uri packageUri = intent.getData();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 8c29a25..441dbac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -16,7 +16,7 @@
*/
package com.android.packageinstaller;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.Manifest;
import android.annotation.NonNull;
@@ -281,7 +281,7 @@
@Override
protected void onCreate(Bundle icicle) {
- getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d60dbe7..5161344 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -2,16 +2,14 @@
name: "SettingsLib",
- libs: [
+ static_libs: [
"androidx.annotation_annotation",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
"androidx.preference_preference",
"androidx.appcompat_appcompat",
"androidx.lifecycle_lifecycle-runtime",
- ],
- static_libs: [
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
"SettingsLibAppPreference",
diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
index 7306d968..e407d72 100644
--- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
@@ -187,17 +187,17 @@
if (sendPackageName && includePackageName) {
String[] packageNameKey =
- {resources.getString(android.R.string.config_help_package_name_key)};
+ {resources.getString(android.R.string.config_helpPackageNameKey)};
String[] packageNameValue =
- {resources.getString(android.R.string.config_help_package_name_value)};
+ {resources.getString(android.R.string.config_helpPackageNameValue)};
String helpIntentExtraKey =
- resources.getString(android.R.string.config_help_intent_extra_key);
+ resources.getString(android.R.string.config_helpIntentExtraKey);
String helpIntentNameKey =
- resources.getString(android.R.string.config_help_intent_name_key);
+ resources.getString(android.R.string.config_helpIntentNameKey);
String feedbackIntentExtraKey =
- resources.getString(android.R.string.config_feedback_intent_extra_key);
+ resources.getString(android.R.string.config_feedbackIntentExtraKey);
String feedbackIntentNameKey =
- resources.getString(android.R.string.config_feedback_intent_name_key);
+ resources.getString(android.R.string.config_feedbackIntentNameKey);
intent.putExtra(helpIntentExtraKey, packageNameKey);
intent.putExtra(helpIntentNameKey, packageNameValue);
intent.putExtra(feedbackIntentExtraKey, packageNameKey);
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
index 0f02abd..0748192 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/layout/restricted_icon.xml
@@ -15,7 +15,7 @@
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/restricted_icon"
- android:layout_width="@*android:dimen/config_restricted_icon_size"
- android:layout_height="@*android:dimen/config_restricted_icon_size"
+ android:layout_width="@*android:dimen/config_restrictedIconSize"
+ android:layout_height="@*android:dimen/config_restrictedIconSize"
android:tint="?android:attr/colorAccent"
android:src="@*android:drawable/ic_info" />
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index b87c9e8..8529e3e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -80,24 +80,17 @@
if (admin.component != null) {
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
}
- int adminUserId = UserHandle.myUserId();
- if (admin.user != null) {
- adminUserId = admin.user.getIdentifier();
- }
- intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
+ final UserHandle adminUser = admin.user != null
+ ? admin.user
+ : UserHandle.of(UserHandle.myUserId());
+ intent.putExtra(Intent.EXTRA_USER, adminUser);
}
return intent;
}
public static boolean isCurrentUserOrProfile(Context context, int userId) {
UserManager um = context.getSystemService(UserManager.class);
- int[] userIds = um.getProfileIds(UserHandle.myUserId(), true);
- for (int i = 0; i < userIds.length; i++) {
- if (userIds[i] == userId) {
- return true;
- }
- }
- return false;
+ return um.getUserProfiles().contains(UserHandle.of(userId));
}
public static class EnforcedAdmin {
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
index af30425..cbebbb3 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
@@ -18,11 +18,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingMode="stack">
<item>
- <shape>
+ <shape
+ android:tint="?android:attr/colorForeground">
<corners
android:radius="20dp"/>
+ <solid
+ android:color="@android:color/transparent"/>
<stroke
- android:color="?android:attr/textColorSecondary"
+ android:color="#1f000000"
android:width="1dp"/>
<size
android:height="32dp"/>
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
index e1f6cdf..5dbcb79 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_widget.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
@@ -16,8 +16,8 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/restricted_icon"
- android:layout_width="@*android:dimen/config_restricted_icon_size"
- android:layout_height="@*android:dimen/config_restricted_icon_size"
+ android:layout_width="@*android:dimen/config_restrictedIconSize"
+ android:layout_height="@*android:dimen/config_restrictedIconSize"
android:tint="?android:attr/colorAccent"
android:src="@*android:drawable/ic_info"
android:gravity="end|center_vertical" />
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index f57122e..1457fcf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -58,7 +58,7 @@
public static Drawable getRestrictedPadlock(Context context) {
Drawable restrictedPadlock = context.getDrawable(android.R.drawable.ic_info);
final int iconSize = context.getResources().getDimensionPixelSize(
- android.R.dimen.config_restricted_icon_size);
+ android.R.dimen.config_restrictedIconSize);
TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
int colorAccent = ta.getColor(0, 0);
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index 3102239..3c45112 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -25,7 +25,6 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.IconDrawableFactory;
import com.android.settingslib.widget.CandidateInfo;
@@ -46,8 +45,8 @@
this(context, pm, uid, cn, null /* summary */, true /* enabled */);
}
- public DefaultAppInfo(Context context, PackageManager pm, PackageItemInfo info) {
- this(context, pm, info, null /* summary */, true /* enabled */);
+ public DefaultAppInfo(Context context, PackageManager pm, int uid, PackageItemInfo info) {
+ this(context, pm, uid, info, null /* summary */, true /* enabled */);
}
public DefaultAppInfo(Context context, PackageManager pm, int uid, ComponentName cn,
@@ -61,12 +60,12 @@
this.summary = summary;
}
- public DefaultAppInfo(Context context, PackageManager pm, PackageItemInfo info,
+ public DefaultAppInfo(Context context, PackageManager pm, int uid, PackageItemInfo info,
String summary, boolean enabled) {
super(enabled);
mContext = context;
mPm = pm;
- userId = UserHandle.myUserId();
+ userId = uid;
packageItemInfo = info;
componentName = null;
this.summary = summary;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 750a843..a2e30df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -60,11 +60,11 @@
private short mRssi;
private final List<LocalBluetoothProfile> mProfiles =
- new ArrayList<LocalBluetoothProfile>();
+ Collections.synchronizedList(new ArrayList<>());
// List of profiles that were previously in mProfiles, but have been removed
private final List<LocalBluetoothProfile> mRemovedProfiles =
- new ArrayList<LocalBluetoothProfile>();
+ Collections.synchronizedList(new ArrayList<>());
// Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
private boolean mLocalNapRoleConnected;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index 1091e16..36b70df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -70,17 +70,17 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mContext.getResources().getString(R.string.config_help_package_name_key))
+ when(mContext.getResources().getString(R.string.config_helpPackageNameKey))
.thenReturn(PACKAGE_NAME_KEY);
- when(mContext.getResources().getString(R.string.config_help_package_name_value))
+ when(mContext.getResources().getString(R.string.config_helpPackageNameValue))
.thenReturn(PACKAGE_NAME_VALUE);
- when(mContext.getResources().getString(R.string.config_help_intent_extra_key))
+ when(mContext.getResources().getString(R.string.config_helpIntentExtraKey))
.thenReturn(HELP_INTENT_EXTRA_KEY);
- when(mContext.getResources().getString(R.string.config_help_intent_name_key))
+ when(mContext.getResources().getString(R.string.config_helpIntentNameKey))
.thenReturn(HELP_INTENT_NAME_KEY);
- when(mContext.getResources().getString(R.string.config_feedback_intent_extra_key))
+ when(mContext.getResources().getString(R.string.config_feedbackIntentExtraKey))
.thenReturn(FEEDBACK_INTENT_EXTRA_KEY);
- when(mContext.getResources().getString(R.string.config_feedback_intent_name_key))
+ when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey))
.thenReturn(FEEDBACK_INTENT_NAME_KEY);
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
index 01f0d78..a92a2dd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -18,8 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -72,7 +72,7 @@
@Test
public void initInfoWithActivityInfo_shouldLoadInfo() {
mPackageItemInfo.packageName = "test";
- mInfo = new DefaultAppInfo(mContext, mPackageManager, mPackageItemInfo);
+ mInfo = new DefaultAppInfo(mContext, mPackageManager, 0 /* uid */, mPackageItemInfo);
mInfo.loadLabel();
Drawable icon = mInfo.loadIcon();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 8745a33..c996620 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -35,6 +35,13 @@
static void dumpProtoLocked(SettingsProvider.SettingsRegistry settingsRegistry,
ProtoOutputStream proto) {
+ // Config settings
+ SettingsState configSettings = settingsRegistry.getSettingsLocked(
+ SettingsProvider.SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+ if (configSettings != null) {
+ // TODO(b/113100523): dump configuration settings after they are added
+ }
+
// Global settings
SettingsState globalSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
@@ -662,6 +669,9 @@
dumpSetting(s, p,
Settings.Global.ANGLE_ENABLED_APP,
GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
+ dumpSetting(s, p,
+ Settings.Global.GPU_DEBUG_LAYER_APP,
+ GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
p.end(gpuToken);
final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 18ec9c3..e0c4d72 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -180,6 +180,7 @@
public static final int SETTINGS_TYPE_SYSTEM = SettingsState.SETTINGS_TYPE_SYSTEM;
public static final int SETTINGS_TYPE_SECURE = SettingsState.SETTINGS_TYPE_SECURE;
public static final int SETTINGS_TYPE_SSAID = SettingsState.SETTINGS_TYPE_SSAID;
+ public static final int SETTINGS_TYPE_CONFIG = SettingsState.SETTINGS_TYPE_CONFIG;
private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
Settings.NameValueTable.VALUE, null);
@@ -189,6 +190,13 @@
private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS = new ArraySet<>();
+ /**
+ * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+ * API.
+ */
+ private static final Uri CONFIG_CONTENT_URI =
+ Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
static {
for (String name : Resources.getSystem().getStringArray(
com.android.internal.R.array.config_allowedGlobalInstantAppSettings)) {
@@ -380,6 +388,11 @@
public Bundle call(String method, String name, Bundle args) {
final int requestingUserId = getRequestingUserId(args);
switch (method) {
+ case Settings.CALL_METHOD_GET_CONFIG: {
+ Setting setting = getConfigSetting(name);
+ return packageValueForCallResult(setting, isTrackingGeneration(args));
+ }
+
case Settings.CALL_METHOD_GET_GLOBAL: {
Setting setting = getGlobalSetting(name);
return packageValueForCallResult(setting, isTrackingGeneration(args));
@@ -396,6 +409,14 @@
return packageValueForCallResult(setting, isTrackingGeneration(args));
}
+ case Settings.CALL_METHOD_PUT_CONFIG: {
+ String value = getSettingValue(args);
+ String tag = getSettingTag(args);
+ final boolean makeDefault = getSettingMakeDefault(args);
+ insertConfigSetting(name, value, tag, makeDefault, requestingUserId, false);
+ break;
+ }
+
case Settings.CALL_METHOD_PUT_GLOBAL: {
String value = getSettingValue(args);
String tag = getSettingTag(args);
@@ -418,6 +439,13 @@
break;
}
+ case Settings.CALL_METHOD_RESET_CONFIG: {
+ final int mode = getResetModeEnforcingPermission(args);
+ String tag = getSettingTag(args);
+ resetConfigSetting(requestingUserId, mode, tag);
+ break;
+ }
+
case Settings.CALL_METHOD_RESET_GLOBAL: {
final int mode = getResetModeEnforcingPermission(args);
String tag = getSettingTag(args);
@@ -725,6 +753,15 @@
@GuardedBy("mLock")
private void dumpForUserLocked(int userId, PrintWriter pw) {
if (userId == UserHandle.USER_SYSTEM) {
+ pw.println("CONFIG SETTINGS (user " + userId + ")");
+ SettingsState configSettings = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+ if (configSettings != null) {
+ dumpSettingsLocked(configSettings, pw);
+ pw.println();
+ configSettings.dumpHistoricalOperations(pw);
+ }
+
pw.println("GLOBAL SETTINGS (user " + userId + ")");
SettingsState globalSettings = mSettingsRegistry.getSettingsLocked(
SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
@@ -939,6 +976,69 @@
});
}
+ private Setting getConfigSetting(String name) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "getConfigSetting(" + name + ")");
+ }
+
+ // TODO(b/117663715): Ensure the caller can access the setting.
+ // enforceSettingReadable(name, SETTINGS_TYPE_CONFIG, UserHandle.getCallingUserId());
+
+ // Get the value.
+ synchronized (mLock) {
+ return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM, name);
+ }
+ }
+
+ private boolean insertConfigSetting(String name, String value, String tag,
+ boolean makeDefault, int requestingUserId, boolean forceNotify) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "insertConfigSetting(" + name + ", " + value + ", "
+ + ", " + tag + ", " + makeDefault + ", " + requestingUserId
+ + ", " + forceNotify + ")");
+ }
+ return mutateConfigSetting(name, value, tag, makeDefault, requestingUserId,
+ MUTATION_OPERATION_INSERT, forceNotify, 0);
+ }
+
+ private void resetConfigSetting(int requestingUserId, int mode, String tag) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
+ + mode + ", " + tag + ")");
+ }
+ mutateConfigSetting(null, null, tag, false, requestingUserId,
+ MUTATION_OPERATION_RESET, false, mode);
+ }
+
+ private boolean mutateConfigSetting(String name, String value, String tag,
+ boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
+ int mode) {
+ // TODO(b/117663715): check the new permission when it's added.
+ // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+
+ // Resolve the userId on whose behalf the call is made.
+ final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
+
+ // Perform the mutation.
+ synchronized (mLock) {
+ switch (operation) {
+ case MUTATION_OPERATION_INSERT: {
+ return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
+ getCallingPackage(), forceNotify, null);
+ }
+
+ case MUTATION_OPERATION_RESET: {
+ mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
+ UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag);
+ } return true;
+ }
+ }
+
+ return false;
+ }
+
private Cursor getAllGlobalSettings(String[] projection) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllGlobalSettings()");
@@ -2132,6 +2232,7 @@
private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";
private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";
private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml";
+ private static final String SETTINGS_FILE_CONFIG = "settings_config.xml";
private static final String SSAID_USER_KEY = "userkey";
@@ -2303,6 +2404,13 @@
// Migrate the setting for this user if needed.
migrateLegacySettingsForUserIfNeededLocked(userId);
+ // Ensure config settings loaded if owner.
+ if (userId == UserHandle.USER_SYSTEM) {
+ final int configKey
+ = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+ ensureSettingsStateLocked(configKey);
+ }
+
// Ensure global settings loaded if owner.
if (userId == UserHandle.USER_SYSTEM) {
final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
@@ -2853,6 +2961,10 @@
}
}
+ private boolean isConfigSettingsKey(int key) {
+ return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG;
+ }
+
private boolean isGlobalSettingsKey(int key) {
return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
}
@@ -2870,7 +2982,11 @@
}
private File getSettingsFile(int key) {
- if (isGlobalSettingsKey(key)) {
+ if (isConfigSettingsKey(key)) {
+ final int userId = getUserIdFromKey(key);
+ return new File(Environment.getUserSystemDirectory(userId),
+ SETTINGS_FILE_CONFIG);
+ } else if (isGlobalSettingsKey(key)) {
final int userId = getUserIdFromKey(key);
return new File(Environment.getUserSystemDirectory(userId),
SETTINGS_FILE_GLOBAL);
@@ -2892,7 +3008,10 @@
}
private Uri getNotificationUriFor(int key, String name) {
- if (isGlobalSettingsKey(key)) {
+ if (isConfigSettingsKey(key)) {
+ return (name != null) ? Uri.withAppendedPath(CONFIG_CONTENT_URI, name)
+ : CONFIG_CONTENT_URI;
+ } else if (isGlobalSettingsKey(key)) {
return (name != null) ? Uri.withAppendedPath(Settings.Global.CONTENT_URI, name)
: Settings.Global.CONTENT_URI;
} else if (isSecureSettingsKey(key)) {
@@ -2908,6 +3027,7 @@
private int getMaxBytesPerPackageForType(int type) {
switch (type) {
+ case SETTINGS_TYPE_CONFIG:
case SETTINGS_TYPE_GLOBAL:
case SETTINGS_TYPE_SECURE:
case SETTINGS_TYPE_SSAID: {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 389d627..ae2ca3f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -34,7 +34,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.providers.settings.GlobalSettingsProto;
import android.providers.settings.SettingsOperationProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -67,7 +66,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
/**
* This class contains the state for one type of settings. It is responsible
@@ -205,6 +203,7 @@
public static final int SETTINGS_TYPE_SYSTEM = 1;
public static final int SETTINGS_TYPE_SECURE = 2;
public static final int SETTINGS_TYPE_SSAID = 3;
+ public static final int SETTINGS_TYPE_CONFIG = 4;
public static final int SETTINGS_TYPE_MASK = 0xF0000000;
public static final int SETTINGS_TYPE_SHIFT = 28;
@@ -223,6 +222,9 @@
public static String settingTypeToString(int type) {
switch (type) {
+ case SETTINGS_TYPE_CONFIG: {
+ return "SETTINGS_CONFIG";
+ }
case SETTINGS_TYPE_GLOBAL: {
return "SETTINGS_GLOBAL";
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index 95569dc..572a924 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -645,7 +645,7 @@
return;
}
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
+ if (elapsedTimeMillis >= WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
fail("Could not change setting for "
+ WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms");
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index dcc6cba..822c39b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -56,6 +56,7 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
+ <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
<!-- Development tool permissions granted to the shell. -->
<uses-permission android:name="android.permission.SET_DEBUG_APP" />
<uses-permission android:name="android.permission.SET_PROCESS_LIMIT" />
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2fbf42f..6378309 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -137,15 +137,9 @@
<integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
<integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
- <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
- <bool name="config_show4GForLTE">true</bool>
-
<!-- Show indicator for Wifi on but not connected. -->
<bool name="config_showWifiIndicatorWhenEnabled">false</bool>
- <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
- <bool name="config_hideLtePlus">false</bool>
-
<!-- The number of milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
index 5a28a5e..7c8c23e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
@@ -48,7 +48,7 @@
if (!mIsWatching) {
try {
WindowManagerGlobal.getWindowManagerService().watchRotation(mWatcher,
- mContext.getDisplay().getDisplayId());
+ mContext.getDisplayId());
mIsWatching = true;
} catch (RemoteException e) {
Log.w(TAG, "Failed to set rotation watcher", e);
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 198a4e6..b1463a3 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -143,9 +143,6 @@
mSeparatedView.setBackground(mSeparatedViewBackground);
updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
mOldHeight = mList.getMeasuredHeight();
- mList.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- updatePosition());
updateRotation();
} else {
return;
@@ -155,6 +152,8 @@
if (newHeight != mOldHeight) {
animateChild(mOldHeight, newHeight);
}
+
+ post(() -> updatePaddingAndGravityIfTooTall());
post(() -> updatePosition());
}
@@ -241,7 +240,7 @@
separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity);
mSeparatedView.setLayoutParams(separatedViewLayoutParams);
- setGravity(p.gravity);
+ setGravity(rotateGravityRight(getGravity()));
}
private void swapDimens(View v) {
@@ -299,7 +298,7 @@
separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity);
mSeparatedView.setLayoutParams(separatedViewLayoutParams);
- setGravity(p.gravity);
+ setGravity(rotateGravityLeft(getGravity()));
}
private int rotateGravityLeft(int gravity) {
@@ -447,6 +446,46 @@
mAnimator.start();
}
+ // If current power menu height larger then screen height, remove padding to break power menu
+ // alignment and set menu center vertical within the screen.
+ private void updatePaddingAndGravityIfTooTall() {
+ int defaultTopPadding;
+ int viewsTotalHeight;
+ int separatedViewTopMargin;
+ int screenHeight;
+ int totalHeight;
+ int targetGravity;
+ MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams();
+ switch (RotationUtils.getRotation(getContext())) {
+ case RotationUtils.ROTATION_LANDSCAPE:
+ defaultTopPadding = getPaddingLeft();
+ viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
+ separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
+ screenHeight = getMeasuredWidth();
+ targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP;
+ break;
+ case RotationUtils.ROTATION_SEASCAPE:
+ defaultTopPadding = getPaddingRight();
+ viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
+ separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
+ screenHeight = getMeasuredWidth();
+ targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM;
+ break;
+ default: // Portrait
+ defaultTopPadding = getPaddingTop();
+ viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight();
+ separatedViewTopMargin = mHasSeparatedButton ? params.topMargin : 0;
+ screenHeight = getMeasuredHeight();
+ targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
+ break;
+ }
+ totalHeight = defaultTopPadding + viewsTotalHeight + separatedViewTopMargin;
+ if (totalHeight >= screenHeight) {
+ setPadding(0, 0, 0, 0);
+ setGravity(targetGravity);
+ }
+ }
+
@Override
public ViewOutlineProvider getOutlineProvider() {
return super.getOutlineProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index d351c4f3..1bf8750 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -259,6 +259,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mHandler.removeCallbacks(mDeferredConnectionCallback);
+ mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
mConnectionBackoffAttempts = 0;
mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
// Listen for launcher's death
@@ -269,7 +270,6 @@
}
try {
mOverviewProxy.onBind(mSysUiProxy);
- mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
} catch (RemoteException e) {
mCurrentBoundedUserId = -1;
Log.e(TAG_OPS, "Failed to call onBind()", e);
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
index 6d79066..449ed8c 100644
--- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -14,7 +14,7 @@
package com.android.systemui;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.app.AlertDialog;
@@ -69,7 +69,7 @@
.setPositiveButton(R.string.slice_permission_allow, this)
.setOnDismissListener(this)
.create();
- dialog.getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ dialog.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
dialog.show();
TextView t1 = dialog.getWindow().getDecorView().findViewById(R.id.text1);
t1.setText(getString(R.string.slice_permission_text_1, app2));
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 0215fda..3fe9944 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -87,6 +87,8 @@
private Runnable mWatchLongPress;
private final long mLongPressTimeout;
+ protected boolean mSwipingInProgress;
+
final private int[] mTmpPos = new int[2];
private final int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
@@ -127,6 +129,10 @@
mDisableHwLayers = disableHwLayers;
}
+ public boolean isSwipingInProgress() {
+ return mSwipingInProgress;
+ }
+
private float getPos(MotionEvent ev) {
return mSwipeDirection == X ? ev.getX() : ev.getY();
}
@@ -318,6 +324,7 @@
if (Math.abs(delta) > mPagingTouchSlop
&& Math.abs(delta) > Math.abs(deltaPerpendicular)) {
if (mCallback.canChildBeDragged(mCurrView)) {
+ mSwipingInProgress = true;
mCallback.onBeginDrag(mCurrView);
mDragging = true;
mInitialTouchPos = getPos(ev);
@@ -437,6 +444,7 @@
wasRemoved = row.isRemoved();
}
if (!mCancelled || wasRemoved) {
+ mSwipingInProgress = false;
mCallback.onChildDismissed(animView);
}
if (endAction != null) {
@@ -626,6 +634,7 @@
!swipedFastEnough() /* useAccelerateInterpolator */);
} else {
// snappity
+ mSwipingInProgress = false;
mCallback.onDragCancelled(mCurrView);
snapChild(mCurrView, 0 /* leftTarget */, velocity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index bb05980..1e61a77 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -47,6 +47,12 @@
void onIgnoreTouchWhilePulsing(boolean ignore);
+ /**
+ * If the device was waken up by a passive interrupt that will show the lock screen without
+ * expanding the notification panel/shade.
+ */
+ void setPassiveInterrupt(boolean lightInterrupt);
+
interface Callback {
/**
* Called when a high priority notification is added.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index cb91d78..d69b1bf 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -142,6 +142,7 @@
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
} else if (isPickup || isWakeLockScreen) {
+ mDozeHost.setPassiveInterrupt(true);
mMachine.wakeUp();
} else {
mDozeHost.extendPulse();
@@ -210,6 +211,7 @@
case INITIALIZED:
mBroadcastReceiver.register(mContext);
mDozeHost.addCallback(mHostCallback);
+ mDozeHost.setPassiveInterrupt(false);
checkTriggersAtInit();
break;
case DOZE:
@@ -219,6 +221,7 @@
mDozeSensors.reregisterAllSensors();
}
mDozeSensors.setListening(true);
+ mDozeHost.setPassiveInterrupt(false);
break;
case DOZE_AOD_PAUSED:
case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 4a67868..df76315 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -16,7 +16,7 @@
package com.android.systemui.media;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.app.AlertDialog;
@@ -151,7 +151,7 @@
((CheckBox) mDialog.findViewById(R.id.remember)).setOnCheckedChangeListener(this);
final Window w = mDialog.getWindow();
w.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- w.addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mDialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
index 774567e..95029c0 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -26,6 +26,10 @@
public class PluginInitializerImpl implements PluginInitializer {
+ /**
+ * True if WTFs should lead to crashes
+ */
+ private static final boolean WTFS_SHOULD_CRASH = false;
private boolean mWtfsSet;
@Override
@@ -52,7 +56,7 @@
@Override
public void handleWtfs() {
- if (!mWtfsSet) {
+ if (WTFS_SHOULD_CRASH && !mWtfsSet) {
mWtfsSet = true;
Log.setWtfHandler(new Log.TerribleFailureHandler() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 92f5cae..15d2e66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -308,6 +308,7 @@
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
+ if (position == RecyclerView.NO_POSITION) return;
if (mAccessibilityAction != ACTION_NONE) {
selectPosition(position, v);
} else {
@@ -561,6 +562,7 @@
if (viewHolder == mCurrentDrag) return;
if (mCurrentDrag != null) {
int position = mCurrentDrag.getAdapterPosition();
+ if (position == RecyclerView.NO_POSITION) return;
TileInfo info = mTiles.get(position);
mCurrentDrag.mTileView.setShowAppLabel(
position > mEditIndex && !info.isSystem);
@@ -582,13 +584,14 @@
@Override
public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
ViewHolder target) {
- if (target.getAdapterPosition() == 0){
+ final int position = target.getAdapterPosition();
+ if (position == 0 || position == RecyclerView.NO_POSITION){
return false;
}
if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) {
- return target.getAdapterPosition() < mEditIndex;
+ return position < mEditIndex;
}
- return target.getAdapterPosition() <= mEditIndex + 1;
+ return position <= mEditIndex + 1;
}
@Override
@@ -610,6 +613,10 @@
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
+ if (from == 0 || from == RecyclerView.NO_POSITION ||
+ to == 0 || to == RecyclerView.NO_POSITION) {
+ return false;
+ }
return move(from, to, target.itemView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 2c384d0..21a33b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
@@ -85,6 +87,7 @@
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, false);
}
+ entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 30d9ef7..8526afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -131,7 +131,7 @@
if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
(int) (y - mInitialTouchY))) {
if (mStartingChild == null) {
- mDragDownCallback.setEmptyDragAmount(0f);
+ cancelExpansion();
} else {
mCallback.setUserLockedChild(mStartingChild, false);
mStartingChild = null;
@@ -206,11 +206,8 @@
ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0);
anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
- anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
- }
+ anim.addUpdateListener(animation -> {
+ mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
});
anim.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0c5f391..a00eac4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,14 +19,14 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
@@ -106,6 +106,7 @@
private final DevicePolicyManager mDevicePolicyManager;
private boolean mDozing;
+ private float mDarkAmount;
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -298,6 +299,15 @@
if (mVisible) {
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
+ if (mDozing) {
+ if (!TextUtils.isEmpty(mTransientIndication)) {
+ mTextView.setTextColor(Color.WHITE);
+ mTextView.switchIndication(mTransientIndication);
+ }
+ updateAlphas();
+ return;
+ }
+
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustGrantedIndication = getTrustGrantedIndication();
@@ -335,6 +345,14 @@
}
}
+ private void updateAlphas() {
+ if (!TextUtils.isEmpty(mTransientIndication)) {
+ mTextView.setAlpha(1f);
+ } else {
+ mTextView.setAlpha(1f - mDarkAmount);
+ }
+ }
+
// animates textView - textView moves up and bounces down
private void animateText(KeyguardIndicationTextView textView, String indication) {
int yTranslation = mContext.getResources().getInteger(
@@ -492,6 +510,14 @@
pw.println(" computePowerIndication(): " + computePowerIndication());
}
+ public void setDarkAmount(float darkAmount) {
+ if (mDarkAmount == darkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ updateAlphas();
+ }
+
protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
public static final int HIDE_DELAY_MS = 5000;
private int mLastSuccessiveErrorMessage = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index e89e6e8..2db9945 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -256,9 +256,9 @@
private boolean isMediaNotification(NotificationData.Entry entry) {
// TODO: confirm that there's a valid media key
- return entry.getExpandedContentView() != null &&
- entry.getExpandedContentView()
- .findViewById(com.android.internal.R.id.media_actions) != null;
+ return entry.row.getExpandedContentView() != null
+ && entry.row.getExpandedContentView().findViewById(
+ com.android.internal.R.id.media_actions) != null;
}
private void clearCurrentMediaNotificationSession() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7fa0426..1495abf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -793,7 +793,7 @@
private void setOpenedAmount(float openedAmount) {
mNoAnimationsInThisFrame = openedAmount == 1.0f && mOpenedAmount == 0.0f;
mOpenedAmount = openedAmount;
- if (!mAmbientState.isPanelFullWidth()) {
+ if (!mAmbientState.isPanelFullWidth() || mAmbientState.isDark()) {
// We don't do a transformation at all, lets just assume we are fully opened
openedAmount = 1.0f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index f69ad43..5b3082b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -327,6 +327,17 @@
entry.notification) && !entry.row.isRemoved();
boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
.notification);
+ if (!showOnKeyguard) {
+ // min priority notifications should show if their summary is showing
+ if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
+ ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+ entry.notification);
+ if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
+ summary.getStatusBarNotification())) {
+ showOnKeyguard = true;
+ }
+ }
+ }
if (suppressedSummary
|| mLockscreenUserManager.shouldHideNotifications(userId)
|| (isLocked && !showOnKeyguard)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index d6719f0..78a5817 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -22,6 +22,7 @@
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Comparator;
@@ -39,6 +40,7 @@
= (o1, o2) -> Integer.compare(o1.rank, o2.rank);
private final ArrayList<RankedListener> mListeners = new ArrayList<>();
+ private boolean mIsDozing;
private int mState;
private int mLastState;
private boolean mLeaveOpenOnKeyguardHide;
@@ -57,6 +59,11 @@
return mState;
}
+ /**
+ * Update the status bar state
+ * @param state see {@link StatusBarState} for valid options
+ * @return {@code true} if the state changed, else {@code false}
+ */
public boolean setState(int state) {
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
@@ -82,6 +89,32 @@
return true;
}
+ public boolean isDozing() {
+ return mIsDozing;
+ }
+
+ /**
+ * Update the dozing state from {@link StatusBar}'s perspective
+ * @param isDozing well, are we dozing?
+ * @return {@code true} if the state changed, else {@code false}
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public boolean setIsDozing(boolean isDozing) {
+ if (mIsDozing == isDozing) {
+ return false;
+ }
+
+ mIsDozing = isDozing;
+
+ synchronized (mListeners) {
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onDozingChanged(isDozing);
+ }
+ }
+
+ return true;
+ }
+
public boolean goingToFullShade() {
return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
}
@@ -144,16 +177,6 @@
return StatusBarState.toShortString(state);
}
- public interface StateListener {
- public default void onStatePreChange(int oldState, int newState) {
- }
-
- public default void onStatePostChange() {
- }
-
- public void onStateChanged(int newState);
- }
-
private class RankedListener {
private final StateListener listener;
private final int rank;
@@ -163,4 +186,40 @@
rank = r;
}
}
+
+ /**
+ * Listener for StatusBarState updates
+ */
+ public interface StateListener {
+
+ /**
+ * Callback before the new state is applied, for those who need to preempt the change
+ * @param oldState state before the change
+ * @param newState new state to be applied in {@link #onStateChanged}
+ */
+ public default void onStatePreChange(int oldState, int newState) {
+ }
+
+ /**
+ * Callback after all listeners have had a chance to update based on the state change
+ */
+ public default void onStatePostChange() {
+ }
+
+ /**
+ * Required callback. Get the new state and do what you will with it. Keep in mind that
+ * other listeners are typically unordered and don't rely on your work being done before
+ * other peers
+ *
+ * Only called if the state is actually different
+ * @param newState the new {@link StatusBarState}
+ */
+ public void onStateChanged(int newState);
+
+ /**
+ * Callback to be notified when Dozing changes. Dozing is stored separately from state.
+ * @param isDozing {@code true} if dozing according to {@link StatusBar}
+ */
+ public default void onDozingChanged(boolean isDozing) {}
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index d097c8e..fbf12ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -50,7 +50,6 @@
import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
-import android.widget.RemoteViews;
import androidx.annotation.Nullable;
@@ -102,11 +101,6 @@
public boolean autoRedacted; // whether the redacted notification was generated by us
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
- public RemoteViews cachedContentView;
- public RemoteViews cachedBigContentView;
- public RemoteViews cachedHeadsUpContentView;
- public RemoteViews cachedPublicContentView;
- public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
@@ -178,14 +172,6 @@
}
}
- public View getExpandedContentView() {
- return row.getPrivateLayout().getExpandedChild();
- }
-
- public View getPublicContentView() {
- return row.getPublicLayout().getContractedChild();
- }
-
public void notifyFullScreenIntentLaunched() {
setInterruption();
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a3e982e..28d339a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,6 +18,10 @@
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.NotificationRemoteInputManager
.FORCE_REMOTE_INPUT_HISTORY;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.app.Notification;
@@ -71,6 +75,7 @@
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -440,25 +445,48 @@
}
private void addEntry(NotificationData.Entry shadeEntry) {
- if (shouldHeadsUp(shadeEntry)) {
- mHeadsUpManager.showNotification(shadeEntry);
- // Mark as seen immediately
- setNotificationShown(shadeEntry.notification);
- }
- if (shouldPulse(shadeEntry)) {
- mAmbientPulseManager.showNotification(shadeEntry);
- }
addNotificationViews(shadeEntry);
mCallback.onNotificationAdded(shadeEntry);
}
+ /**
+ * Adds the entry to the respective alerting manager if the content view was inflated and
+ * the entry should still alert.
+ *
+ * @param entry entry to add
+ * @param inflatedFlags flags representing content views that were inflated
+ */
+ private void showAlertingView(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags) {
+ if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
+ // Possible for shouldHeadsUp to change between the inflation starting and ending.
+ // If it does and we no longer need to heads up, we should free the view.
+ if (shouldHeadsUp(entry)) {
+ mHeadsUpManager.showNotification(entry);
+ // Mark as seen immediately
+ setNotificationShown(entry.notification);
+ } else {
+ entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ }
+ }
+ if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
+ if (shouldPulse(entry)) {
+ mAmbientPulseManager.showNotification(entry);
+ } else {
+ entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ }
+ }
+ }
+
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags) {
mPendingNotifications.remove(entry.key);
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
boolean isNew = mNotificationData.get(entry.key) == null;
if (isNew && !entry.row.isRemoved()) {
+ showAlertingView(entry, inflatedFlags);
addEntry(entry);
} else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
mVisualStabilityManager.onLowPriorityUpdated(entry);
@@ -636,7 +664,11 @@
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setSmartActions(entry.smartActions);
- row.updateNotification(entry);
+ row.setEntry(entry);
+
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, shouldPulse(entry));
+ row.inflateViews();
}
protected void addNotificationViews(NotificationData.Entry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 81208c4..53ebe74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -24,7 +24,10 @@
public interface VisibilityLocationProvider {
/**
- * @return whether the view is in a visible location right now.
+ * Returns whether an ExpandableNotificationRow is in a visible location or not.
+ *
+ * @param row
+ * @return true if row is in a visible location
*/
boolean isInVisibleLocation(ExpandableNotificationRow row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index bce613a..8110c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,12 +17,19 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -83,6 +90,7 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -94,6 +102,9 @@
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
@@ -429,15 +440,62 @@
}
}
- public void updateNotification(NotificationData.Entry entry) {
+ /**
+ * Set the entry for the row.
+ *
+ * @param entry the entry this row is tied to
+ */
+ public void setEntry(@NonNull NotificationData.Entry entry) {
mEntry = entry;
mStatusBarNotification = entry.notification;
- mNotificationInflater.inflateNotificationViews();
-
cacheIsSystemNotification();
}
/**
+ * Inflate views based off the inflation flags set. Inflation happens asynchronously.
+ */
+ public void inflateViews() {
+ mNotificationInflater.inflateNotificationViews();
+ }
+
+ /**
+ * Marks a content view as freeable, setting it so that future inflations do not reinflate
+ * and ensuring that the view is freed when it is safe to remove.
+ *
+ * @param inflationFlag flag corresponding to the content view to be freed
+ */
+ public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+ // View should not be reinflated in the future
+ updateInflationFlag(inflationFlag, false);
+ Runnable freeViewRunnable = () ->
+ mNotificationInflater.freeNotificationView(inflationFlag);
+ switch (inflationFlag) {
+ case FLAG_CONTENT_VIEW_HEADS_UP:
+ getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
+ freeViewRunnable);
+ break;
+ case FLAG_CONTENT_VIEW_AMBIENT:
+ getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT,
+ freeViewRunnable);
+ getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT,
+ freeViewRunnable);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Update whether or not a content view should be inflated.
+ *
+ * @param flag the flag corresponding to the content view
+ * @param shouldInflate true if it should be inflated, false if it should not
+ */
+ public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
+ mNotificationInflater.updateInflationFlag(flag, shouldInflate);
+ }
+
+ /**
* Caches whether or not this row contains a system notification. Note, this is only cached
* once per notification as the packageInfo can't technically change for a notification row.
*/
@@ -581,7 +639,7 @@
headsUpHeight = mMaxHeadsUpHeight;
}
NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_HEADSUP);
+ VISIBLE_TYPE_HEADSUP);
if (headsUpWrapper != null) {
headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
}
@@ -2616,6 +2674,10 @@
return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
}
+ public View getExpandedContentView() {
+ return getPrivateLayout().getExpandedChild();
+ }
+
public void setLegacy(boolean legacy) {
for (NotificationContentView l : mLayouts) {
l.setLegacy(legacy);
@@ -3017,6 +3079,36 @@
boolean onClick(View v, int x, int y, MenuItem item);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ pw.println(" Notification: " + getStatusBarNotification().getKey());
+ pw.print(" visibility: " + getVisibility());
+ pw.print(", alpha: " + getAlpha());
+ pw.print(", translation: " + getTranslation());
+ pw.print(", removed: " + isRemoved());
+ pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+ pw.println();
+ pw.print(" ");
+ if (mNotificationViewState != null) {
+ mNotificationViewState.dump(fd, pw, args);
+ } else {
+ pw.print("no viewState!!!");
+ }
+ pw.println();
+ pw.println();
+ if (mIsSummaryWithChildren) {
+ List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
+ pw.println(" Children: " + notificationChildren.size());
+ pw.println(" {");
+ for(ExpandableNotificationRow child : notificationChildren) {
+ child.dump(fd, pw, args);
+ }
+ pw.println(" }");
+ pw.println();
+ }
+ }
+
/**
* Background task for executing IPCs to check if the notification is a system notification. The
* output is used for both the blocking helper and the notification info.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 46019e3..38d657b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -25,16 +25,19 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
/**
* An abstract view for expandable views.
*/
-public abstract class ExpandableView extends FrameLayout {
+public abstract class ExpandableView extends FrameLayout implements Dumpable {
public static final float NO_ROUNDNESS = -1;
protected OnHeightChangedListener mOnHeightChangedListener;
@@ -559,6 +562,10 @@
return false;
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ }
+
/**
* A listener notifying when {@link #getActualHeight} changes.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 4ef8dbb..7856451 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
@@ -108,6 +109,10 @@
private NotificationGroupManager mGroupManager;
private RemoteInputController mRemoteInputController;
private Runnable mExpandedVisibleListener;
+ /**
+ * List of listeners for when content views become inactive (i.e. not the showing view).
+ */
+ private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
= new ViewTreeObserver.OnPreDrawListener() {
@@ -517,6 +522,14 @@
removeView(mAmbientChild);
}
if (child == null) {
+ mAmbientChild = null;
+ mAmbientWrapper = null;
+ if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
+ mVisibleType = VISIBLE_TYPE_CONTRACTED;
+ }
+ if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
+ mTransformationStartVisibleType = UNDEFINED;
+ }
return;
}
addView(child);
@@ -1163,6 +1176,7 @@
public void onNotificationUpdated(NotificationData.Entry entry) {
mStatusBarNotification = entry.notification;
+ mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
if (mContractedChild != null) {
@@ -1620,6 +1634,58 @@
fireExpandedVisibleListenerIfVisible();
}
+ /**
+ * Set a one-shot listener to run when a given content view becomes inactive.
+ *
+ * @param visibleType visible type corresponding to the content view to listen
+ * @param listener runnable to run once when the content view becomes inactive
+ */
+ public void performWhenContentInactive(int visibleType, Runnable listener) {
+ View view = getViewForVisibleType(visibleType);
+ // View is already inactive
+ if (view == null || isContentViewInactive(visibleType)) {
+ listener.run();
+ return;
+ }
+ mOnContentViewInactiveListeners.put(view, listener);
+ }
+
+ /**
+ * Whether or not the content view is inactive. This means it should not be visible
+ * or the showing content as removing it would cause visual jank.
+ *
+ * @param visibleType visible type corresponding to the content view to be removed
+ * @return true if the content view is inactive, false otherwise
+ */
+ public boolean isContentViewInactive(int visibleType) {
+ View view = getViewForVisibleType(visibleType);
+ return isContentViewInactive(view);
+ }
+
+ /**
+ * Whether or not the content view is inactive.
+ *
+ * @param view view to see if its inactive
+ * @return true if the view is inactive, false o/w
+ */
+ private boolean isContentViewInactive(View view) {
+ if (view == null) {
+ return true;
+ }
+ return view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view;
+ }
+
+ @Override
+ protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
+ super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
+ if (isContentViewInactive(child)) {
+ Runnable listener = mOnContentViewInactiveListeners.remove(child);
+ if (listener != null) {
+ listener.run();
+ }
+ }
+ }
+
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index aa4765a..ea1892b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -16,12 +16,17 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -35,6 +40,8 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.Assert;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -52,14 +59,64 @@
public class NotificationInflater {
public static final String TAG = "NotificationInflater";
- @VisibleForTesting
- static final int FLAG_REINFLATE_ALL = ~0;
- private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
- @VisibleForTesting
- static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
- private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
- private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
- private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true,
+ prefix = {"FLAG_CONTENT_VIEW_"},
+ value = {
+ FLAG_CONTENT_VIEW_CONTRACTED,
+ FLAG_CONTENT_VIEW_EXPANDED,
+ FLAG_CONTENT_VIEW_HEADS_UP,
+ FLAG_CONTENT_VIEW_AMBIENT,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ FLAG_CONTENT_VIEW_ALL})
+ public @interface InflationFlag {}
+ /**
+ * The default, contracted view. Seen when the shade is pulled down and in the lock screen
+ * if there is no worry about content sensitivity.
+ */
+ public static final int FLAG_CONTENT_VIEW_CONTRACTED = 1;
+
+ /**
+ * The expanded view. Seen when the user expands a notification.
+ */
+ public static final int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1;
+
+ /**
+ * The heads up view. Seen when a high priority notification peeks in from the top.
+ */
+ public static final int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2;
+
+ /**
+ * The ambient view. Seen when a high priority notification is received and the phone
+ * is dozing.
+ */
+ public static final int FLAG_CONTENT_VIEW_AMBIENT = 1 << 3;
+
+ /**
+ * The public view. This is a version of the contracted view that hides sensitive
+ * information and is used on the lock screen if we determine that the notification's
+ * content should be hidden.
+ */
+ public static final int FLAG_CONTENT_VIEW_PUBLIC = 1 << 4;
+
+ public static final int FLAG_CONTENT_VIEW_ALL = ~0;
+
+ /**
+ * Content views that must be inflated at all times.
+ */
+ @InflationFlag
+ private static final int REQUIRED_INFLATION_FLAGS =
+ FLAG_CONTENT_VIEW_CONTRACTED
+ | FLAG_CONTENT_VIEW_EXPANDED
+ | FLAG_CONTENT_VIEW_PUBLIC;
+
+ /**
+ * The set of content views to inflate.
+ */
+ @InflationFlag
+ private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
+
private static final InflationExecutor EXECUTOR = new InflationExecutor();
private final ExpandableNotificationRow mRow;
@@ -71,6 +128,7 @@
private InflationCallback mCallback;
private boolean mRedactAmbient;
private List<Notification.Action> mSmartActions;
+ private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
public NotificationInflater(ExpandableNotificationRow row) {
mRow = row;
@@ -89,10 +147,10 @@
if (childInGroup != mIsChildInGroup) {
mIsChildInGroup = childInGroup;
if (mIsLowPriority) {
- int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
+ int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
inflateNotificationViews(flags);
}
- } ;
+ }
}
public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
@@ -117,38 +175,67 @@
if (mRow.getEntry() == null) {
return;
}
- inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+ inflateNotificationViews(FLAG_CONTENT_VIEW_AMBIENT);
}
}
/**
+ * Set whether or not a particular content view is needed and whether or not it should be
+ * inflated. These flags will be used when we inflate or reinflate.
+ *
+ * @param flag the {@link InflationFlag} corresponding to the view that should/should not be
+ * inflated
+ * @param shouldInflate true if the view should be inflated, false otherwise
+ */
+ public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
+ if (shouldInflate) {
+ mInflationFlags |= flag;
+ } else if ((REQUIRED_INFLATION_FLAGS & flag) == 0) {
+ mInflationFlags &= ~flag;
+ }
+ }
+
+ /**
+ * Add flags for which content views should be inflated in addition to those already set.
+ *
+ * @param flags a set of {@link InflationFlag} corresponding to content views that should be
+ * inflated
+ */
+ public void addInflationFlags(@InflationFlag int flags) {
+ mInflationFlags |= flags;
+ }
+
+ /**
* Inflate all views of this notification on a background thread. This is asynchronous and will
* notify the callback once it's finished.
*/
public void inflateNotificationViews() {
- inflateNotificationViews(FLAG_REINFLATE_ALL);
+ inflateNotificationViews(mInflationFlags);
}
/**
- * Reinflate all views for the specified flags on a background thread. This is asynchronous and
- * will notify the callback once it's finished.
+ * Inflate all views for the specified flags on a background thread. This is asynchronous and
+ * will notify the callback once it's finished. If the content view is already inflated, this
+ * will reinflate it.
*
- * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
- * to reinflate all of views.
+ * @param reInflateFlags flags which views should be inflated. Should be a subset of
+ * {@link NotificationInflater#mInflationFlags} as only those will be
+ * inflated/reinflated.
*/
- @VisibleForTesting
- void inflateNotificationViews(int reInflateFlags) {
+ private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
if (mRow.isRemoved()) {
// We don't want to reinflate anything for removed notifications. Otherwise views might
// be readded to the stack, leading to leaks. This may happen with low-priority groups
// where the removal of already removed children can lead to a reinflation.
return;
}
+ // Only inflate the ones that are set.
+ reInflateFlags |= mInflationFlags;
StatusBarNotification sbn = mRow.getEntry().notification;
- AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
- mIsLowPriority,
- mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
- mCallback, mRemoteViewClickHandler, mSmartActions);
+ AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
+ mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
+ mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler,
+ mSmartActions);
if (mCallback != null && mCallback.doInflateSynchronous()) {
task.onPostExecute(task.doInBackground());
} else {
@@ -157,38 +244,80 @@
}
@VisibleForTesting
- InflationProgress inflateNotificationViews(int reInflateFlags,
+ InflationProgress inflateNotificationViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, Context packageContext) {
InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
mRedactAmbient, packageContext);
- apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null);
+ apply(result, reInflateFlags, mCachedContentViews, mRow, mRedactAmbient,
+ mRemoteViewClickHandler, null);
return result;
}
- private static InflationProgress createRemoteViews(int reInflateFlags,
+ /**
+ * Frees the content view associated with the inflation flag. Will only succeed if the
+ * view is safe to remove.
+ *
+ * @param inflateFlag the flag corresponding to the content view which should be freed
+ */
+ public void freeNotificationView(@InflationFlag int inflateFlag) {
+ if ((mInflationFlags & inflateFlag) != 0) {
+ // The view should still be inflated.
+ return;
+ }
+ switch (inflateFlag) {
+ case FLAG_CONTENT_VIEW_HEADS_UP:
+ if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
+ mRow.getPrivateLayout().setHeadsUpChild(null);
+ mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
+ }
+ break;
+ case FLAG_CONTENT_VIEW_AMBIENT:
+ boolean privateSafeToRemove = mRow.getPrivateLayout().isContentViewInactive(
+ VISIBLE_TYPE_AMBIENT);
+ boolean publicSafeToRemove = mRow.getPublicLayout().isContentViewInactive(
+ VISIBLE_TYPE_AMBIENT);
+ if (privateSafeToRemove) {
+ mRow.getPrivateLayout().setAmbientChild(null);
+ }
+ if (publicSafeToRemove) {
+ mRow.getPublicLayout().setAmbientChild(null);
+ }
+ if (privateSafeToRemove && publicSafeToRemove) {
+ mCachedContentViews.remove(FLAG_CONTENT_VIEW_AMBIENT);
+ }
+ break;
+ case FLAG_CONTENT_VIEW_CONTRACTED:
+ case FLAG_CONTENT_VIEW_EXPANDED:
+ case FLAG_CONTENT_VIEW_PUBLIC:
+ default:
+ break;
+ }
+ }
+
+ private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
Context packageContext) {
InflationProgress result = new InflationProgress();
isLowPriority = isLowPriority && !isChildInGroup;
- if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
}
- if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
result.newExpandedView = createExpandedView(builder, isLowPriority);
}
- if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
}
- if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
result.newPublicView = builder.makePublicContentView();
}
- if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
: builder.makeAmbientNotification();
}
@@ -199,18 +328,20 @@
return result;
}
- public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
+ public static CancellationSignal apply(InflationProgress result,
+ @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
ExpandableNotificationRow row, boolean redactAmbient,
RemoteViews.OnClickHandler remoteViewClickHandler,
@Nullable InflationCallback callback) {
- NotificationData.Entry entry = row.getEntry();
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
- int flag = FLAG_REINFLATE_CONTENT_VIEW;
+ int flag = FLAG_CONTENT_VIEW_CONTRACTED;
if ((reInflateFlags & flag) != 0) {
- boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newContentView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -222,18 +353,19 @@
return result.newContentView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
- isNewView, remoteViewClickHandler, callback, entry, privateLayout,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, redactAmbient,
+ isNewView, remoteViewClickHandler, callback, privateLayout,
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
}
- flag = FLAG_REINFLATE_EXPANDED_VIEW;
+ flag = FLAG_CONTENT_VIEW_EXPANDED;
if ((reInflateFlags & flag) != 0) {
if (result.newExpandedView != null) {
- boolean isNewView = !canReapplyRemoteView(result.newExpandedView,
- entry.cachedBigContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newExpandedView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -245,8 +377,8 @@
return result.newExpandedView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
privateLayout, privateLayout.getExpandedChild(),
privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -254,11 +386,12 @@
}
}
- flag = FLAG_REINFLATE_HEADS_UP_VIEW;
+ flag = FLAG_CONTENT_VIEW_HEADS_UP;
if ((reInflateFlags & flag) != 0) {
if (result.newHeadsUpView != null) {
- boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView,
- entry.cachedHeadsUpContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newHeadsUpView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -270,19 +403,20 @@
return result.newHeadsUpView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
privateLayout, privateLayout.getHeadsUpChild(),
privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations,
+ VISIBLE_TYPE_HEADSUP), runningInflations,
applyCallback);
}
}
- flag = FLAG_REINFLATE_PUBLIC_VIEW;
+ flag = FLAG_CONTENT_VIEW_PUBLIC;
if ((reInflateFlags & flag) != 0) {
- boolean isNewView = !canReapplyRemoteView(result.newPublicView,
- entry.cachedPublicContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newPublicView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -294,18 +428,19 @@
return result.newPublicView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
}
- flag = FLAG_REINFLATE_AMBIENT_VIEW;
+ flag = FLAG_CONTENT_VIEW_AMBIENT;
if ((reInflateFlags & flag) != 0) {
NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
- boolean isNewView = !canReapplyAmbient(row, redactAmbient) ||
- !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView);
+ boolean isNewView = (!canReapplyAmbient(row, redactAmbient)
+ || !canReapplyRemoteView(result.newAmbientView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_AMBIENT)));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -317,15 +452,15 @@
return result.newAmbientView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations,
applyCallback);
}
// Let's try to finish, maybe nobody is even inflating anything
- finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+ finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row,
redactAmbient);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(
@@ -335,11 +470,11 @@
@VisibleForTesting
static void applyRemoteView(final InflationProgress result,
- final int reInflateFlags, int inflationId,
- final ExpandableNotificationRow row,
- final boolean redactAmbient, boolean isNewView,
+ final @InflationFlag int reInflateFlags, @InflationFlag int inflationId,
+ final ArrayMap<Integer, RemoteViews> cachedContentViews,
+ final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView,
RemoteViews.OnClickHandler remoteViewClickHandler,
- @Nullable final InflationCallback callback, NotificationData.Entry entry,
+ @Nullable final InflationCallback callback,
NotificationContentView parentLayout, View existingView,
NotificationViewWrapper existingWrapper,
final HashMap<Integer, CancellationSignal> runningInflations,
@@ -362,7 +497,7 @@
existingWrapper.onReinflated();
}
} catch (Exception e) {
- handleInflationError(runningInflations, e, entry.notification, callback);
+ handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback);
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
runningInflations.put(inflationId, new CancellationSignal());
@@ -381,8 +516,8 @@
existingWrapper.onReinflated();
}
runningInflations.remove(inflationId);
- finishIfDone(result, reInflateFlags, runningInflations, callback, row,
- redactAmbient);
+ finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
+ callback, row, redactAmbient);
}
@Override
@@ -407,7 +542,8 @@
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, entry.notification, callback);
+ handleInflationError(runningInflations, e, row.getStatusBarNotification(),
+ callback);
}
}
};
@@ -430,8 +566,9 @@
runningInflations.put(inflationId, cancellationSignal);
}
- private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations,
- Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) {
+ private static void handleInflationError(
+ HashMap<Integer, CancellationSignal> runningInflations, Exception e,
+ StatusBarNotification notification, @Nullable InflationCallback callback) {
Assert.isMainThread();
runningInflations.values().forEach(CancellationSignal::cancel);
if (callback != null) {
@@ -444,7 +581,8 @@
*
* @return true if the inflation was finished
*/
- private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
+ private static boolean finishIfDone(InflationProgress result,
+ @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
HashMap<Integer, CancellationSignal> runningInflations,
@Nullable InflationCallback endListener, ExpandableNotificationRow row,
boolean redactAmbient) {
@@ -453,40 +591,40 @@
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
if (runningInflations.isEmpty()) {
- if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
if (result.inflatedContentView != null) {
privateLayout.setContractedChild(result.inflatedContentView);
}
- entry.cachedContentView = result.newContentView;
+ cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
}
- if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
if (result.inflatedExpandedView != null) {
privateLayout.setExpandedChild(result.inflatedExpandedView);
} else if (result.newExpandedView == null) {
privateLayout.setExpandedChild(null);
}
- entry.cachedBigContentView = result.newExpandedView;
+ cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
row.setExpandable(result.newExpandedView != null);
}
- if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
if (result.inflatedHeadsUpView != null) {
privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
} else if (result.newHeadsUpView == null) {
privateLayout.setHeadsUpChild(null);
}
- entry.cachedHeadsUpContentView = result.newHeadsUpView;
+ cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
}
- if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
if (result.inflatedPublicView != null) {
publicLayout.setContractedChild(result.inflatedPublicView);
}
- entry.cachedPublicContentView = result.newPublicView;
+ cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
}
- if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+ if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
if (result.inflatedAmbientView != null) {
NotificationContentView newParent = redactAmbient
? publicLayout : privateLayout;
@@ -495,12 +633,12 @@
newParent.setAmbientChild(result.inflatedAmbientView);
otherParent.setAmbientChild(null);
}
- entry.cachedAmbientContentView = result.newAmbientView;
+ cachedContentViews.put(FLAG_CONTENT_VIEW_AMBIENT, result.newAmbientView);
}
entry.headsUpStatusBarText = result.headsUpStatusBarText;
entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
- endListener.onAsyncInflationFinished(row.getEntry());
+ endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
}
return true;
}
@@ -552,7 +690,15 @@
public interface InflationCallback {
void handleInflationException(StatusBarNotification notification, Exception e);
- void onAsyncInflationFinished(NotificationData.Entry entry);
+
+ /**
+ * Callback for after the content views finish inflating.
+ *
+ * @param entry the entry with the content views set
+ * @param inflatedFlags the flags associated with the content views that were inflated
+ */
+ void onAsyncInflationFinished(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags);
/**
* Used to disable async-ness for tests. Should only be used for tests.
@@ -563,18 +709,13 @@
}
public void clearCachesAndReInflate() {
- NotificationData.Entry entry = mRow.getEntry();
- entry.cachedAmbientContentView = null;
- entry.cachedBigContentView = null;
- entry.cachedContentView = null;
- entry.cachedHeadsUpContentView = null;
- entry.cachedPublicContentView = null;
+ mCachedContentViews.clear();
inflateNotificationViews();
}
private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
- : row.getPrivateLayout(); ;
+ : row.getPrivateLayout();
return ambientView.getAmbientChild() != null;
}
@@ -589,7 +730,8 @@
private final InflationCallback mCallback;
private final boolean mUsesIncreasedHeadsUpHeight;
private final boolean mRedactAmbient;
- private int mReInflateFlags;
+ private @InflationFlag int mReInflateFlags;
+ private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
@@ -597,15 +739,16 @@
private List<Notification.Action> mSmartActions;
private AsyncInflationTask(StatusBarNotification notification,
- int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
- boolean isChildInGroup, boolean usesIncreasedHeight,
+ @InflationFlag int reInflateFlags,
+ ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row,
+ boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
- InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler,
+ InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler,
List<Notification.Action> smartActions) {
mRow = row;
mSbn = notification;
mReInflateFlags = reInflateFlags;
+ mCachedContentViews = cachedContentViews;
mContext = mRow.getContext();
mIsLowPriority = isLowPriority;
mIsChildInGroup = isChildInGroup;
@@ -622,6 +765,7 @@
}
@VisibleForTesting
+ @InflationFlag
public int getReInflateFlags() {
return mReInflateFlags;
}
@@ -642,10 +786,9 @@
packageContext);
processor.processNotification(notification, recoveredBuilder);
}
- return createRemoteViews(mReInflateFlags,
- recoveredBuilder, mIsLowPriority, mIsChildInGroup,
- mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
- packageContext);
+ return createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority,
+ mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
+ mRedactAmbient, packageContext);
} catch (Exception e) {
mError = e;
return null;
@@ -655,8 +798,8 @@
@Override
protected void onPostExecute(InflationProgress result) {
if (mError == null) {
- mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
- mRemoteViewClickHandler, this);
+ mCancellationSignal = apply(result, mReInflateFlags, mCachedContentViews, mRow,
+ mRedactAmbient, mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
@@ -706,10 +849,11 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags) {
mRow.getEntry().onInflationTaskFinished();
mRow.onNotificationUpdated();
- mCallback.onAsyncInflationFinished(mRow.getEntry());
+ mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 2ca7282..f76284d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.view.NotificationHeaderView;
@@ -76,8 +77,11 @@
}
Drawable background = mView.getBackground();
if (background instanceof ColorDrawable) {
- mBackgroundColor = ((ColorDrawable) background).getColor();
- mView.setBackground(null);
+ int backgroundColor = ((ColorDrawable) background).getColor();
+ if (backgroundColor != Color.TRANSPARENT) {
+ mBackgroundColor = backgroundColor;
+ mView.setBackground(new ColorDrawable(Color.TRANSPARENT));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index fa75c71..cfb6d99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -22,6 +22,7 @@
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -31,7 +32,8 @@
* Interface representing the entity that contains notifications. It can have
* notification views added and removed from it, and will manage displaying them to the user.
*/
-public interface NotificationListContainer {
+public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
+ VisibilityLocationProvider {
/**
* Called when a child is being transferred.
@@ -128,14 +130,6 @@
ViewGroup getViewParentForNotification(NotificationData.Entry entry);
/**
- * Called when the height of an expandable view changes.
- *
- * @param view view whose height changed
- * @param animate whether this change should be animated
- */
- void onHeightChanged(ExpandableView view, boolean animate);
-
- /**
* Resets the currently exposed menu view.
*
* @param animate whether to animate the closing/change of menu view
@@ -158,13 +152,6 @@
*/
void cleanUpViewState(View view);
- /**
- * Returns whether an ExpandableNotificationRow is in a visible location or not.
- *
- * @param row
- * @return true if row is in a visible location
- */
- boolean isInVisibleLocation(ExpandableNotificationRow row);
/**
* Sets a listener to listen for changes in notification locations.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 33ac390..659f6c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -25,6 +25,8 @@
import android.animation.PropertyValuesHolder;
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WallpaperManager;
import android.content.Context;
@@ -43,10 +45,6 @@
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.ColorUtils;
-
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -69,6 +67,8 @@
import android.widget.OverScroller;
import android.widget.ScrollView;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
@@ -84,6 +84,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
@@ -117,7 +118,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone.AnimationStateHandler;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -142,10 +143,8 @@
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
-public class NotificationStackScrollLayout extends ViewGroup
- implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener,
- OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer,
- ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable {
+public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
+ NotificationListContainer, ConfigurationListener, Dumpable {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -160,7 +159,6 @@
private ExpandHelper mExpandHelper;
private final NotificationSwipeHelper mSwipeHelper;
- private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
private final Paint mBackgroundPaint = new Paint();
private final boolean mShouldDrawNotificationBackground;
@@ -344,7 +342,7 @@
private float mDimAmount;
private ValueAnimator mDimAnimator;
private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
- private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+ private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mDimAnimator = null;
@@ -485,12 +483,12 @@
mBgColor = context.getColor(R.color.notification_shade_background_color);
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
- mExpandHelper = new ExpandHelper(getContext(), this,
+ mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
- mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(),
- getContext(), new NotificationMenuListener());
+ mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
+ getContext(), mMenuEventListener);
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
@@ -530,7 +528,7 @@
inflateEmptyShadeView();
inflateFooterView();
- mVisualStabilityManager.setVisibilityLocationProvider(this);
+ mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
setLongPressListener(mEntryManager.getNotificationLongClicker());
}
@@ -589,7 +587,7 @@
return false;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationData.Entry entry,
@@ -628,7 +626,7 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
@@ -1245,11 +1243,6 @@
return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
- mLongPressListener = listener;
- }
-
@ShadeViewRefactor(RefactorComponent.ADAPTER)
public void setQsContainer(ViewGroup qsContainer) {
mQsContainer = qsContainer;
@@ -1273,7 +1266,7 @@
return false;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
float localTouchY = touchY - mTempInt2[1];
@@ -1303,16 +1296,8 @@
return closestChild;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
- getLocationOnScreen(mTempInt2);
- return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public ExpandableView getChildAtPosition(float touchX, float touchY) {
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+ private ExpandableView getChildAtPosition(float touchX, float touchY) {
return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
}
@@ -1325,7 +1310,7 @@
* @param requireMinHeight Whether a minimum height is required for a child to be returned.
* @return the child at the given location.
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ExpandableView getChildAtPosition(float touchX, float touchY,
boolean requireMinHeight) {
// find the view under the pointer, accounting for GONE views
@@ -1365,71 +1350,9 @@
return null;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- public boolean canChildBeExpanded(View v) {
- return v instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) v).isExpandable()
- && !((ExpandableNotificationRow) v).areGutsExposed()
- && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
- }
-
- /* Only ever called as a consequence of an expansion gesture in the shade. */
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setUserExpandedChild(View v, boolean userExpanded) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (userExpanded && onKeyguard()) {
- // Due to a race when locking the screen while touching, a notification may be
- // expanded even after we went back to keyguard. An example of this happens if
- // you click in the empty space while expanding a group.
-
- // We also need to un-user lock it here, since otherwise the content height
- // calculated might be wrong. We also can't invert the two calls since
- // un-userlocking it will trigger a layout switch in the content view.
- row.setUserLocked(false);
- updateContentHeight();
- notifyHeightChangeListener(row);
- return;
- }
- row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
- row.onExpandedByGesture(userExpanded);
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setExpansionCancelled(View v) {
- if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setUserLockedChild(View v, boolean userLocked) {
- if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserLocked(userLocked);
- }
- cancelLongPress();
- requestDisallowInterceptTouchEvent(true);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void expansionStateChanged(boolean isExpanding) {
- mExpandingNotification = isExpanding;
- if (!mExpandedInThisMotion) {
- mMaxScrollAfterExpand = mOwnScrollY;
- mExpandedInThisMotion = true;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public int getMaxExpandHeight(ExpandableView view) {
- return view.getMaxContentHeight();
+ private ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+ getLocationOnScreen(mTempInt2);
+ return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -1526,14 +1449,6 @@
return mStatusBarState == StatusBarState.KEYGUARD;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void setSwipingInProgress(boolean isSwiped) {
- mSwipingInProgress = isSwiped;
- if (isSwiped) {
- requestDisallowInterceptTouchEvent(true);
- }
- }
-
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onConfigurationChanged(Configuration newConfig) {
@@ -1567,249 +1482,6 @@
return this;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onTouchEvent(MotionEvent ev) {
- boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
- || ev.getActionMasked() == MotionEvent.ACTION_UP;
- handleEmptySpaceClick(ev);
- boolean expandWantsIt = false;
- if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
- if (isCancelOrUp) {
- mExpandHelper.onlyObserveMovements(false);
- }
- boolean wasExpandingBefore = mExpandingNotification;
- expandWantsIt = mExpandHelper.onTouchEvent(ev);
- if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
- && !mDisallowScrollingInThisMotion) {
- dispatchDownEventToScroller(ev);
- }
- }
- boolean scrollerWantsIt = false;
- if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
- && !mDisallowScrollingInThisMotion) {
- scrollerWantsIt = onScrollTouch(ev);
- }
- boolean horizontalSwipeWantsIt = false;
- if (!mIsBeingDragged
- && !mExpandingNotification
- && !mExpandedInThisMotion
- && !mOnlyScrollingInThisMotion
- && !mDisallowDismissInThisMotion) {
- horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
- }
-
- // Check if we need to clear any snooze leavebehinds
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
- if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
- && guts.getGutsContent() instanceof NotificationSnooze) {
- NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
- if ((ns.isExpanded() && isCancelOrUp)
- || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
- // If the leavebehind is expanded we clear it on the next up event, otherwise we
- // clear it on the next non-horizontal swipe or expand event.
- checkSnoozeLeavebehind();
- }
- }
- if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
- mCheckForLeavebehind = true;
- }
- return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void dispatchDownEventToScroller(MotionEvent ev) {
- MotionEvent downEvent = MotionEvent.obtain(ev);
- downEvent.setAction(MotionEvent.ACTION_DOWN);
- onScrollTouch(downEvent);
- downEvent.recycle();
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onGenericMotionEvent(MotionEvent event) {
- if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
- || mDisallowScrollingInThisMotion) {
- return false;
- }
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_SCROLL: {
- if (!mIsBeingDragged) {
- final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
- if (vscroll != 0) {
- final int delta = (int) (vscroll * getVerticalScrollFactor());
- final int range = getScrollRange();
- int oldScrollY = mOwnScrollY;
- int newScrollY = oldScrollY - delta;
- if (newScrollY < 0) {
- newScrollY = 0;
- } else if (newScrollY > range) {
- newScrollY = range;
- }
- if (newScrollY != oldScrollY) {
- setOwnScrollY(newScrollY);
- return true;
- }
- }
- }
- }
- }
- }
- return super.onGenericMotionEvent(event);
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private boolean onScrollTouch(MotionEvent ev) {
- if (!isScrollingEnabled()) {
- return false;
- }
- if (isInsideQsContainer(ev) && !mIsBeingDragged) {
- return false;
- }
- mForcedScroll = null;
- initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
-
- final int action = ev.getAction();
-
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- if (getChildCount() == 0 || !isInContentBounds(ev)) {
- return false;
- }
- boolean isBeingDragged = !mScroller.isFinished();
- setIsBeingDragged(isBeingDragged);
- /*
- * If being flinged and user touches, stop the fling. isFinished
- * will be false if being flinged.
- */
- if (!mScroller.isFinished()) {
- mScroller.forceFinished(true);
- }
-
- // Remember where the motion event started
- mLastMotionY = (int) ev.getY();
- mDownX = (int) ev.getX();
- mActivePointerId = ev.getPointerId(0);
- break;
- }
- case MotionEvent.ACTION_MOVE:
- final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- if (activePointerIndex == -1) {
- Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
- break;
- }
-
- final int y = (int) ev.getY(activePointerIndex);
- final int x = (int) ev.getX(activePointerIndex);
- int deltaY = mLastMotionY - y;
- final int xDiff = Math.abs(x - mDownX);
- final int yDiff = Math.abs(deltaY);
- if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
- setIsBeingDragged(true);
- if (deltaY > 0) {
- deltaY -= mTouchSlop;
- } else {
- deltaY += mTouchSlop;
- }
- }
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- mLastMotionY = y;
- int range = getScrollRange();
- if (mExpandedInThisMotion) {
- range = Math.min(range, mMaxScrollAfterExpand);
- }
-
- float scrollAmount;
- if (deltaY < 0) {
- scrollAmount = overScrollDown(deltaY);
- } else {
- scrollAmount = overScrollUp(deltaY, range);
- }
-
- // Calling customOverScrollBy will call onCustomOverScrolled, which
- // sets the scrolling if applicable.
- if (scrollAmount != 0.0f) {
- // The scrolling motion could not be compensated with the
- // existing overScroll, we have to scroll the view
- customOverScrollBy((int) scrollAmount, mOwnScrollY,
- range, getHeight() / 2);
- // If we're scrolling, leavebehinds should be dismissed
- checkSnoozeLeavebehind();
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mIsBeingDragged) {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
- if (shouldOverScrollFling(initialVelocity)) {
- onOverScrollFling(true, initialVelocity);
- } else {
- if (getChildCount() > 0) {
- if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
- float currentOverScrollTop = getCurrentOverScrollAmount(true);
- if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
- fling(-initialVelocity);
- } else {
- onOverScrollFling(false, initialVelocity);
- }
- } else {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
- getScrollRange())) {
- animateScroll();
- }
- }
- }
- }
- mActivePointerId = INVALID_POINTER;
- endDrag();
- }
-
- break;
- case MotionEvent.ACTION_CANCEL:
- if (mIsBeingDragged && getChildCount() > 0) {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
- animateScroll();
- }
- mActivePointerId = INVALID_POINTER;
- endDrag();
- }
- break;
- case MotionEvent.ACTION_POINTER_DOWN: {
- final int index = ev.getActionIndex();
- mLastMotionY = (int) ev.getY(index);
- mDownX = (int) ev.getX(index);
- mActivePointerId = ev.getPointerId(index);
- break;
- }
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
- mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
- break;
- }
- return true;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- protected boolean isInsideQsContainer(MotionEvent ev) {
- return ev.getY() < mQsContainer.getBottom();
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void onOverScrollFling(boolean open, int initialVelocity) {
- if (mOverscrollTopChangedListener != null) {
- mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
- }
- mDontReportNextOverScroll = true;
- setOverScrollAmount(0.0f, true, false);
- }
-
/**
* Perform a scroll upwards and adapt the overscroll amounts accordingly
*
@@ -1817,7 +1489,7 @@
* @return The amount of scrolling to be performed by the scroller,
* not handled by the overScroll amount.
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private float overScrollUp(int deltaY, int range) {
deltaY = Math.max(deltaY, 0);
float currentTopAmount = getCurrentOverScrollAmount(true);
@@ -1876,24 +1548,6 @@
return scrollAmount;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
- MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- // TODO: Make this decision more intelligent.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionY = (int) ev.getY(newPointerIndex);
- mActivePointerId = ev.getPointerId(newPointerIndex);
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
@@ -2636,7 +2290,7 @@
* numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void fling(int velocityY) {
if (getChildCount() > 0) {
int scrollRange = getScrollRange();
@@ -2674,7 +2328,7 @@
* @return Whether a fling performed on the top overscroll edge lead to the expanded
* overScroll view (i.e QS).
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean shouldOverScrollFling(int initialVelocity) {
float topOverScroll = getCurrentOverScrollAmount(true);
return mScrolledToTopOnFirstDown
@@ -2757,7 +2411,7 @@
return Math.max(desiredPadding, mIntrinsicPadding);
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private float getRubberBandFactor(boolean onTop) {
if (!onTop) {
return RUBBER_BAND_FACTOR_NORMAL;
@@ -2777,99 +2431,13 @@
* rubberbanded, false if it is technically an overscroll but rather a motion to expand the
* overscroll view (e.g. expand QS).
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean isRubberbanded(boolean onTop) {
return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
|| !mScrolledToTopOnFirstDown;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void endDrag() {
- setIsBeingDragged(false);
- recycleVelocityTracker();
-
- if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
- setOverScrollAmount(0, true /* onTop */, true /* animate */);
- }
- if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
- setOverScrollAmount(0, false /* onTop */, true /* animate */);
- }
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
- ev.offsetLocation(sourceView.getX(), sourceView.getY());
- ev.offsetLocation(-targetView.getX(), -targetView.getY());
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- initDownStates(ev);
- handleEmptySpaceClick(ev);
- boolean expandWantsIt = false;
- if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
- expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
- }
- boolean scrollWantsIt = false;
- if (!mSwipingInProgress && !mExpandingNotification) {
- scrollWantsIt = onInterceptTouchEventScroll(ev);
- }
- boolean swipeWantsIt = false;
- if (!mIsBeingDragged
- && !mExpandingNotification
- && !mExpandedInThisMotion
- && !mOnlyScrollingInThisMotion
- && !mDisallowDismissInThisMotion) {
- swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
- }
- // Check if we need to clear any snooze leavebehinds
- boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
- if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
- !expandWantsIt && !scrollWantsIt) {
- mCheckForLeavebehind = false;
- mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
- false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- }
- if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
- mCheckForLeavebehind = true;
- }
- return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void handleEmptySpaceClick(MotionEvent ev) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
- || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
- mTouchIsClick = false;
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
- isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
- mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
- }
- break;
- }
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void initDownStates(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mExpandedInThisMotion = false;
- mOnlyScrollingInThisMotion = !mScroller.isFinished();
- mDisallowScrollingInThisMotion = false;
- mDisallowDismissInThisMotion = false;
- mTouchIsClick = true;
- mInitialTouchX = ev.getX();
- mInitialTouchY = ev.getY();
- }
- }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -2896,15 +2464,6 @@
mCurrentStackScrollState.removeViewStateForView(child);
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- if (disallowIntercept) {
- cancelLongPress();
- }
- }
-
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void onViewRemovedInternal(View child, ViewGroup container) {
if (mChangePositionInProgress) {
@@ -3600,6 +3159,385 @@
mGoToFullShadeNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
+ protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
+ return new StackScrollAlgorithm(context);
+ }
+
+ /**
+ * @return Whether a y coordinate is inside the content.
+ */
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public boolean isInContentBounds(float y) {
+ return y < getHeight() - getEmptyBottomMargin();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+ mLongPressListener = listener;
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || ev.getActionMasked() == MotionEvent.ACTION_UP;
+ handleEmptySpaceClick(ev);
+ boolean expandWantsIt = false;
+ boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+ if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
+ if (isCancelOrUp) {
+ mExpandHelper.onlyObserveMovements(false);
+ }
+ boolean wasExpandingBefore = mExpandingNotification;
+ expandWantsIt = mExpandHelper.onTouchEvent(ev);
+ if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
+ && !mDisallowScrollingInThisMotion) {
+ dispatchDownEventToScroller(ev);
+ }
+ }
+ boolean scrollerWantsIt = false;
+ if (mIsExpanded && !swipingInProgress && !mExpandingNotification
+ && !mDisallowScrollingInThisMotion) {
+ scrollerWantsIt = onScrollTouch(ev);
+ }
+ boolean horizontalSwipeWantsIt = false;
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion
+ && !mDisallowDismissInThisMotion) {
+ horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+ }
+
+ // Check if we need to clear any snooze leavebehinds
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
+ && guts.getGutsContent() instanceof NotificationSnooze) {
+ NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+ if ((ns.isExpanded() && isCancelOrUp)
+ || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+ // If the leavebehind is expanded we clear it on the next up event, otherwise we
+ // clear it on the next non-horizontal swipe or expand event.
+ checkSnoozeLeavebehind();
+ }
+ }
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCheckForLeavebehind = true;
+ }
+ return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void dispatchDownEventToScroller(MotionEvent ev) {
+ MotionEvent downEvent = MotionEvent.obtain(ev);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ onScrollTouch(downEvent);
+ downEvent.recycle();
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification
+ || mDisallowScrollingInThisMotion) {
+ return false;
+ }
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ if (!mIsBeingDragged) {
+ final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ if (vscroll != 0) {
+ final int delta = (int) (vscroll * getVerticalScrollFactor());
+ final int range = getScrollRange();
+ int oldScrollY = mOwnScrollY;
+ int newScrollY = oldScrollY - delta;
+ if (newScrollY < 0) {
+ newScrollY = 0;
+ } else if (newScrollY > range) {
+ newScrollY = range;
+ }
+ if (newScrollY != oldScrollY) {
+ setOwnScrollY(newScrollY);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private boolean onScrollTouch(MotionEvent ev) {
+ if (!isScrollingEnabled()) {
+ return false;
+ }
+ if (isInsideQsContainer(ev) && !mIsBeingDragged) {
+ return false;
+ }
+ mForcedScroll = null;
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ if (getChildCount() == 0 || !isInContentBounds(ev)) {
+ return false;
+ }
+ boolean isBeingDragged = !mScroller.isFinished();
+ setIsBeingDragged(isBeingDragged);
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.forceFinished(true);
+ }
+
+ // Remember where the motion event started
+ mLastMotionY = (int) ev.getY();
+ mDownX = (int) ev.getX();
+ mActivePointerId = ev.getPointerId(0);
+ break;
+ }
+ case MotionEvent.ACTION_MOVE:
+ final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (activePointerIndex == -1) {
+ Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+ break;
+ }
+
+ final int y = (int) ev.getY(activePointerIndex);
+ final int x = (int) ev.getX(activePointerIndex);
+ int deltaY = mLastMotionY - y;
+ final int xDiff = Math.abs(x - mDownX);
+ final int yDiff = Math.abs(deltaY);
+ if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+ setIsBeingDragged(true);
+ if (deltaY > 0) {
+ deltaY -= mTouchSlop;
+ } else {
+ deltaY += mTouchSlop;
+ }
+ }
+ if (mIsBeingDragged) {
+ // Scroll to follow the motion event
+ mLastMotionY = y;
+ int range = getScrollRange();
+ if (mExpandedInThisMotion) {
+ range = Math.min(range, mMaxScrollAfterExpand);
+ }
+
+ float scrollAmount;
+ if (deltaY < 0) {
+ scrollAmount = overScrollDown(deltaY);
+ } else {
+ scrollAmount = overScrollUp(deltaY, range);
+ }
+
+ // Calling customOverScrollBy will call onCustomOverScrolled, which
+ // sets the scrolling if applicable.
+ if (scrollAmount != 0.0f) {
+ // The scrolling motion could not be compensated with the
+ // existing overScroll, we have to scroll the view
+ customOverScrollBy((int) scrollAmount, mOwnScrollY,
+ range, getHeight() / 2);
+ // If we're scrolling, leavebehinds should be dismissed
+ checkSnoozeLeavebehind();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mIsBeingDragged) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+ if (shouldOverScrollFling(initialVelocity)) {
+ onOverScrollFling(true, initialVelocity);
+ } else {
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ float currentOverScrollTop = getCurrentOverScrollAmount(true);
+ if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ fling(-initialVelocity);
+ } else {
+ onOverScrollFling(false, initialVelocity);
+ }
+ } else {
+ if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ getScrollRange())) {
+ animateScroll();
+ }
+ }
+ }
+ }
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ }
+
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsBeingDragged && getChildCount() > 0) {
+ if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+ animateScroll();
+ }
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int index = ev.getActionIndex();
+ mLastMotionY = (int) ev.getY(index);
+ mDownX = (int) ev.getX(index);
+ mActivePointerId = ev.getPointerId(index);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+ mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
+ break;
+ }
+ return true;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ protected boolean isInsideQsContainer(MotionEvent ev) {
+ return ev.getY() < mQsContainer.getBottom();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void onOverScrollFling(boolean open, int initialVelocity) {
+ if (mOverscrollTopChangedListener != null) {
+ mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+ }
+ mDontReportNextOverScroll = true;
+ setOverScrollAmount(0.0f, true, false);
+ }
+
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+ MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ // TODO: Make this decision more intelligent.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastMotionY = (int) ev.getY(newPointerIndex);
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void endDrag() {
+ setIsBeingDragged(false);
+
+ recycleVelocityTracker();
+
+ if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+ setOverScrollAmount(0, true /* onTop */, true /* animate */);
+ }
+ if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+ setOverScrollAmount(0, false /* onTop */, true /* animate */);
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
+ ev.offsetLocation(sourceView.getX(), sourceView.getY());
+ ev.offsetLocation(-targetView.getX(), -targetView.getY());
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ initDownStates(ev);
+ handleEmptySpaceClick(ev);
+ boolean expandWantsIt = false;
+ boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+ if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
+ expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+ }
+ boolean scrollWantsIt = false;
+ if (!swipingInProgress && !mExpandingNotification) {
+ scrollWantsIt = onInterceptTouchEventScroll(ev);
+ }
+ boolean swipeWantsIt = false;
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion
+ && !mDisallowDismissInThisMotion) {
+ swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+ }
+ // Check if we need to clear any snooze leavebehinds
+ boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+ !expandWantsIt && !scrollWantsIt) {
+ mCheckForLeavebehind = false;
+ mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ }
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCheckForLeavebehind = true;
+ }
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void handleEmptySpaceClick(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
+ || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
+ mTouchIsClick = false;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
+ isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
+ mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
+ }
+ break;
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void initDownStates(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mExpandedInThisMotion = false;
+ mOnlyScrollingInThisMotion = !mScroller.isFinished();
+ mDisallowScrollingInThisMotion = false;
+ mDisallowDismissInThisMotion = false;
+ mTouchIsClick = true;
+ mInitialTouchX = ev.getX();
+ mInitialTouchY = ev.getY();
+ }
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ if (disallowIntercept) {
+ cancelLongPress();
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.INPUT)
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
if (!isScrollingEnabled()) {
@@ -3710,11 +3648,6 @@
return mIsBeingDragged;
}
- @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context);
- }
-
/**
* @return Whether the specified motion event is actually happening over the content.
*/
@@ -3723,13 +3656,6 @@
return isInContentBounds(event.getY());
}
- /**
- * @return Whether a y coordinate is inside the content.
- */
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean isInContentBounds(float y) {
- return y < getHeight() - getEmptyBottomMargin();
- }
@VisibleForTesting
@ShadeViewRefactor(RefactorComponent.INPUT)
@@ -3742,6 +3668,83 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void requestDisallowLongPress() {
+ cancelLongPress();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void requestDisallowDismiss() {
+ mDisallowDismissInThisMotion = true;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void cancelLongPress() {
+ mSwipeHelper.cancelLongPress();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
+ mOnEmptySpaceClickListener = listener;
+ }
+
+ /** @hide */
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (super.performAccessibilityActionInternal(action, arguments)) {
+ return true;
+ }
+ if (!isEnabled()) {
+ return false;
+ }
+ int direction = -1;
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ // fall through
+ case android.R.id.accessibilityActionScrollDown:
+ direction = 1;
+ // fall through
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ // fall through
+ case android.R.id.accessibilityActionScrollUp:
+ final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+ - mShelf.getIntrinsicHeight();
+ final int targetScrollY = Math.max(0,
+ Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+ if (targetScrollY != mOwnScrollY) {
+ mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+ animateScroll();
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+ View translatingParentView = mSwipeHelper.getTranslatingParentView();
+ View view = null;
+ if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+ // Only close visible guts if they're not a leavebehind.
+ view = guts;
+ } else if (menuRow != null && menuRow.isMenuVisible()
+ && translatingParentView != null) {
+ // Checking menu
+ view = translatingParentView;
+ }
+ if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+ // Touch was outside visible guts / menu notification, close what's visible
+ mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ resetExposedMenuView(true /* animate */, true /* force */);
+ }
+ }
+
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -3760,21 +3763,6 @@
}
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void requestDisallowLongPress() {
- cancelLongPress();
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void requestDisallowDismiss() {
- mDisallowDismissInThisMotion = true;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void cancelLongPress() {
- mSwipeHelper.cancelLongPress();
- }
-
@Override
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public boolean isScrolledToTop() {
@@ -3916,7 +3904,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
@@ -3936,7 +3923,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onReset(ExpandableView view) {
updateAnimationState(view);
updateChronometerForChild(view);
@@ -3969,13 +3955,8 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setOnHeightChangedListener(
- ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
- this.mOnHeightChangedListener = mOnHeightChangedListener;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
- mOnEmptySpaceClickListener = listener;
+ ExpandableView.OnHeightChangedListener onHeightChangedListener) {
+ this.mOnHeightChangedListener = onHeightChangedListener;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4465,7 +4446,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setGroupManager(NotificationGroupManager groupManager) {
this.mGroupManager = groupManager;
- mGroupManager.setOnGroupChangeListener(this);
+ mGroupManager.setOnGroupChangeListener(mOnGroupChangeListener);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4508,33 +4489,6 @@
return touchY > mTopPadding + mStackTranslation;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
- boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
- && (mIsExpanded || changedRow.isPinned());
- if (animated) {
- mExpandedGroupView = changedRow;
- mNeedsAnimation = true;
- }
- changedRow.setChildrenExpanded(expanded, animated);
- if (!mGroupExpandedForMeasure) {
- onHeightChanged(changedRow, false /* needsAnimation */);
- }
- runAfterAnimationFinished(new Runnable() {
- @Override
- public void run() {
- changedRow.onFinishedExpansionChange();
- }
- });
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
- mStatusBar.requestNotificationUpdate();
- }
-
/** @hide */
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4567,46 +4521,6 @@
info.setClassName(ScrollView.class.getName());
}
- /** @hide */
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (super.performAccessibilityActionInternal(action, arguments)) {
- return true;
- }
- if (!isEnabled()) {
- return false;
- }
- int direction = -1;
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
- // fall through
- case android.R.id.accessibilityActionScrollDown:
- direction = 1;
- // fall through
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
- // fall through
- case android.R.id.accessibilityActionScrollUp:
- final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
- - mShelf.getIntrinsicHeight();
- final int targetScrollY = Math.max(0,
- Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
- if (targetScrollY != mOwnScrollY) {
- mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
- animateScroll();
- return true;
- }
- break;
- }
- return false;
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onGroupsChanged() {
- mStatusBar.requestNotificationUpdate();
- }
-
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void generateChildOrderChangedEvent() {
if (mIsExpanded && mAnimationsEnabled) {
@@ -4649,7 +4563,7 @@
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mHeadsUpManager.addListener(mRoundnessManager);
- mHeadsUpManager.setAnimationStateHandler(this);
+ mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4912,6 +4826,34 @@
mMaxTopPadding,
mShouldShowShelfOnly ? "T" : "f",
mQsExpansionFraction));
+ int childCount = getChildCount();
+ pw.println(" Number of children: " + childCount);
+ pw.println();
+
+ for (int i = 0; i < childCount; i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ child.dump(fd, pw, args);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ pw.println(" " + child.getClass().getSimpleName());
+ // Notifications dump it's viewstate as part of their dump to support children
+ ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(
+ child);
+ if (viewState == null) {
+ pw.println(" no viewState!!!");
+ } else {
+ pw.print(" ");
+ viewState.dump(fd, pw, args);
+ pw.println();
+ pw.println();
+ }
+ }
+ }
+ pw.println(" Transient Views: " + childCount);
+ int transientViewCount = getTransientViewCount();
+ for (int i = 0; i < transientViewCount; i++) {
+ ExpandableView child = (ExpandableView) getTransientView(i);
+ child.dump(fd, pw, args);
+ }
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5140,67 +5082,7 @@
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
-
-
- /* Only ever called as a consequence of a lockscreen expansion gesture. */
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onDraggedDown(View startingChild, int dragLengthY) {
- if (mStatusBarState == StatusBarState.KEYGUARD
- && hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_SHADE,
- (int) (dragLengthY / mDisplayMetrics.density),
- 0 /* velocityDp - N/A */);
-
- // We have notifications, go to locked shade.
- mStatusBar.goToLockedShade(startingChild);
- if (startingChild instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
- row.onExpandedByGesture(true /* drag down is always an open */);
- }
- return true;
- } else {
- // abort gesture.
- return false;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onDragDownReset() {
- setDimmed(true /* dimmed */, true /* animated */);
- resetScrollPosition();
- resetCheckSnoozeLeavebehind();
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onCrossedThreshold(boolean above) {
- setDimmed(!above /* dimmed */, true /* animate */);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onTouchSlopExceeded() {
- cancelLongPress();
- checkSnoozeLeavebehind();
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setEmptyDragAmount(float amount) {
- mNotificationPanel.setEmptyDragAmount(amount);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean isFalsingCheckNeeded() {
- return mStatusBarState == StatusBarState.KEYGUARD;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateSpeedBumpIndex() {
int speedBumpIndex = 0;
int currentIndex = 0;
@@ -5241,30 +5123,6 @@
mSwipeHelper.resetExposedMenuView(animate, force);
}
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void closeControlsIfOutsideTouch(MotionEvent ev) {
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
- NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
- View translatingParentView = mSwipeHelper.getTranslatingParentView();
- View view = null;
- if (guts != null && !guts.getGutsContent().isLeavebehind()) {
- // Only close visible guts if they're not a leavebehind.
- view = guts;
- } else if (menuRow != null && menuRow.isMenuVisible()
- && translatingParentView != null) {
- // Checking menu
- view = translatingParentView;
- }
- if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
- // Touch was outside visible guts / menu notification, close what's visible
- mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
- false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- resetExposedMenuView(true /* animate */, true /* force */);
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
static class AnimationEvent {
@@ -5586,9 +5444,9 @@
}
};
- class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuClicked(View view, int x, int y, MenuItem item) {
if (mLongPressListener == null) {
return;
@@ -5602,7 +5460,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuReset(View row) {
View translatingParentView = mSwipeHelper.getTranslatingParentView();
if (translatingParentView != null && row == translatingParentView) {
@@ -5612,7 +5469,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuShown(View row) {
if (row instanceof ExpandableNotificationRow) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
@@ -5621,9 +5477,11 @@
}
mSwipeHelper.onMenuShown(row);
}
- }
+ };
- class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+ new NotificationSwipeHelper.NotificationCallback() {
@Override
public void onDismiss() {
mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
@@ -5643,10 +5501,8 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onDragCancelled(View v) {
mFalsingManager.onNotificatonStopDismissing();
- setSwipingInProgress(false);
}
/**
@@ -5654,7 +5510,6 @@
* re-invoking dismiss logic in case the notification has not made its way out yet).
*/
@Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onChildDismissed(View view) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (!row.isDismissed()) {
@@ -5673,7 +5528,6 @@
* @param view view (e.g. notification) to dismiss from the layout
*/
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void handleChildViewDismissed(View view) {
if (mDismissAllInProgress) {
return;
@@ -5681,7 +5535,6 @@
boolean isBlockingHelperShown = false;
- setSwipingInProgress(false);
if (mDragAnimPendingChildren.contains(view)) {
// We start the swipe and finish it in the same frame; we don't want a drag
// animation.
@@ -5715,13 +5568,11 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean isAntiFalsingNeeded() {
return onKeyguard();
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public View getChildAtPosition(MotionEvent ev) {
View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
ev.getY());
@@ -5744,10 +5595,8 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
- setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
updateContinuousShadowDrawing();
if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
@@ -5758,7 +5607,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onChildSnappedBack(View animView, float targetLeft) {
mAmbientState.onDragFinished(animView);
updateContinuousShadowDrawing();
@@ -5780,7 +5628,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean updateSwipeProgress(View animView, boolean dismissable,
float swipeProgress) {
// Returning true prevents alpha fading.
@@ -5788,7 +5635,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public float getFalsingThresholdFactor() {
return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@@ -5797,5 +5643,199 @@
public boolean canChildBeDismissed(View v) {
return NotificationStackScrollLayout.this.canChildBeDismissed(v);
}
+ };
+
+ // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private final DragDownCallback mDragDownCallback = new DragDownCallback() {
+
+ /* Only ever called as a consequence of a lockscreen expansion gesture. */
+ @Override
+ public boolean onDraggedDown(View startingChild, int dragLengthY) {
+ if (mStatusBarState == StatusBarState.KEYGUARD
+ && hasActiveNotifications()) {
+ mLockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_SHADE,
+ (int) (dragLengthY / mDisplayMetrics.density),
+ 0 /* velocityDp - N/A */);
+
+ if (mNotificationPanel.onDraggedDown() || startingChild != null) {
+ // We have notifications, go to locked shade.
+ mStatusBar.goToLockedShade(startingChild);
+ if (startingChild instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
+ row.onExpandedByGesture(true /* drag down is always an open */);
+ }
+ }
+
+ return true;
+ } else {
+ // abort gesture.
+ return false;
+ }
+ }
+
+ @Override
+ public void onDragDownReset() {
+ setDimmed(true /* dimmed */, true /* animated */);
+ resetScrollPosition();
+ resetCheckSnoozeLeavebehind();
+ }
+
+ @Override
+ public void onCrossedThreshold(boolean above) {
+ setDimmed(!above /* dimmed */, true /* animate */);
+ }
+
+ @Override
+ public void onTouchSlopExceeded() {
+ cancelLongPress();
+ checkSnoozeLeavebehind();
+ }
+
+ @Override
+ public void setEmptyDragAmount(float amount) {
+ mNotificationPanel.setEmptyDragAmount(amount);
+ }
+
+ @Override
+ public boolean isFalsingCheckNeeded() {
+ return mStatusBarState == StatusBarState.KEYGUARD;
+ }
+ };
+
+ public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
+
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
+ @Override
+ public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+ return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
+ }
+
+ @Override
+ public boolean isExpanded() {
+ return mIsExpanded;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+ };
+
+ public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
+
+
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
+ @Override
+ public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
+ boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
+ && (mIsExpanded || changedRow.isPinned());
+ if (animated) {
+ mExpandedGroupView = changedRow;
+ mNeedsAnimation = true;
+ }
+ changedRow.setChildrenExpanded(expanded, animated);
+ if (!mGroupExpandedForMeasure) {
+ onHeightChanged(changedRow, false /* needsAnimation */);
+ }
+ runAfterAnimationFinished(new Runnable() {
+ @Override
+ public void run() {
+ changedRow.onFinishedExpansionChange();
+ }
+ });
+ }
+
+ @Override
+ public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
+ mStatusBar.requestNotificationUpdate();
+ }
+
+ @Override
+ public void onGroupsChanged() {
+ mStatusBar.requestNotificationUpdate();
+ }
+ };
+
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
+ @Override
+ public ExpandableView getChildAtPosition(float touchX, float touchY) {
+ return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
+ }
+
+ @Override
+ public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+ return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
+ }
+
+ @Override
+ public boolean canChildBeExpanded(View v) {
+ return v instanceof ExpandableNotificationRow
+ && ((ExpandableNotificationRow) v).isExpandable()
+ && !((ExpandableNotificationRow) v).areGutsExposed()
+ && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
+ }
+
+ /* Only ever called as a consequence of an expansion gesture in the shade. */
+ @Override
+ public void setUserExpandedChild(View v, boolean userExpanded) {
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (userExpanded && onKeyguard()) {
+ // Due to a race when locking the screen while touching, a notification may be
+ // expanded even after we went back to keyguard. An example of this happens if
+ // you click in the empty space while expanding a group.
+
+ // We also need to un-user lock it here, since otherwise the content height
+ // calculated might be wrong. We also can't invert the two calls since
+ // un-userlocking it will trigger a layout switch in the content view.
+ row.setUserLocked(false);
+ updateContentHeight();
+ notifyHeightChangeListener(row);
+ return;
+ }
+ row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
+ row.onExpandedByGesture(userExpanded);
+ }
+ }
+
+ @Override
+ public void setExpansionCancelled(View v) {
+ if (v instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
+ }
+ }
+
+ @Override
+ public void setUserLockedChild(View v, boolean userLocked) {
+ if (v instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) v).setUserLocked(userLocked);
+ }
+ cancelLongPress();
+ requestDisallowInterceptTouchEvent(true);
+ }
+
+ @Override
+ public void expansionStateChanged(boolean isExpanding) {
+ mExpandingNotification = isExpanding;
+ if (!mExpandedInThisMotion) {
+ mMaxScrollAfterExpand = mOwnScrollY;
+ mExpandedInThisMotion = true;
+ }
+ }
+
+ @Override
+ public int getMaxExpandHeight(ExpandableView view) {
+ return view.getMaxContentHeight();
+ }
+ };
+
+ public ExpandHelper.Callback getExpandHelperCallback() {
+ return mExpandHelperCallback;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 028957d..599da3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -31,11 +31,9 @@
import com.android.systemui.SwipeHelper;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT)
class NotificationSwipeHelper extends SwipeHelper
implements NotificationSwipeActionHelper {
@VisibleForTesting
@@ -229,6 +227,7 @@
if (mCallback.isExpanded()) {
// We don't want to quick-dismiss when it's a heads up as this might lead to closing
// of the panel early.
+ mSwipingInProgress = false;
mCallback.handleChildViewDismissed(view);
}
mCallback.onDismiss();
@@ -248,6 +247,7 @@
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
superSnapChild(animView, targetLeft, velocity);
+ mSwipingInProgress = false;
mCallback.onDragCancelled(animView);
if (targetLeft == 0) {
handleMenuCoveredOrDismissed();
@@ -354,6 +354,7 @@
public void onMenuShown(View animView) {
setExposedMenuView(getTranslatingParentView());
+ mSwipingInProgress = false;
mCallback.onDragCancelled(animView);
Handler handler = getHandler();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 1f3244f..a15fd70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -25,6 +25,7 @@
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -32,12 +33,17 @@
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
/**
* A state of a view. This can be used to apply a set of view properties to a view with
* {@link com.android.systemui.statusbar.notification.stack.StackScrollState} or start
* animations with {@link com.android.systemui.statusbar.notification.stack.StackStateAnimator}.
*/
-public class ViewState {
+public class ViewState implements Dumpable {
/**
* Some animation properties that can be used to update running animations but not creating
@@ -710,4 +716,39 @@
animator.cancel();
}
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ StringBuilder result = new StringBuilder();
+ result.append("ViewState { ");
+
+ boolean first = true;
+ Class currentClass = this.getClass();
+ while (currentClass != null) {
+ Field[] fields = currentClass.getDeclaredFields();
+ // Print field names paired with their values
+ for (Field field : fields) {
+ int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) || field.isSynthetic()
+ || Modifier.isTransient(modifiers)) {
+ continue;
+ }
+ if (!first) {
+ result.append(", ");
+ }
+ try {
+ result.append(field.getName());
+ result.append(": ");
+ //requires access to private field:
+ field.setAccessible(true);
+ result.append(field.get(this));
+ } catch (IllegalAccessException ex) {
+ }
+ first = false;
+ }
+ currentClass = currentClass.getSuperclass();
+ }
+ result.append(" }");
+ pw.print(result);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 4df1e3b..e4a5caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -32,7 +32,7 @@
public class HeadsUpTouchHelper implements Gefingerpoken {
private HeadsUpManagerPhone mHeadsUpManager;
- private NotificationStackScrollLayout mStackScroller;
+ private Callback mCallback;
private int mTrackingPointer;
private float mTouchSlop;
private float mInitialTouchX;
@@ -44,12 +44,12 @@
private ExpandableNotificationRow mPickedChild;
public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
- NotificationStackScrollLayout stackScroller,
+ Callback callback,
NotificationPanelView notificationPanelView) {
mHeadsUpManager = headsUpManager;
- mStackScroller = stackScroller;
+ mCallback = callback;
mPanel = notificationPanelView;
- Context context = stackScroller.getContext();
+ Context context = mCallback.getContext();
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
}
@@ -75,13 +75,13 @@
mInitialTouchY = y;
mInitialTouchX = x;
setTrackingHeadsUp(false);
- ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
+ ExpandableView child = mCallback.getChildAtRawPosition(x, y);
mTouchingHeadsUpView = false;
if (child instanceof ExpandableNotificationRow) {
mPickedChild = (ExpandableNotificationRow) child;
- mTouchingHeadsUpView = !mStackScroller.isExpanded()
+ mTouchingHeadsUpView = !mCallback.isExpanded()
&& mPickedChild.isHeadsUp() && mPickedChild.isPinned();
- } else if (child == null && !mStackScroller.isExpanded()) {
+ } else if (child == null && !mCallback.isExpanded()) {
// We might touch above the visible heads up child, but then we still would
// like to capture it.
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
@@ -174,4 +174,10 @@
mPickedChild = null;
mTouchingHeadsUpView = false;
}
+
+ public interface Callback {
+ ExpandableView getChildAtRawPosition(float touchX, float touchY);
+ boolean isExpanded();
+ Context getContext();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index ae1353d..072343a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -560,7 +560,7 @@
return;
}
mDarkAmount = darkAmount;
- mIndicationArea.setAlpha(1f - darkAmount);
+ mIndicationController.setDarkAmount(darkAmount);
mLockIcon.setDarkAmount(darkAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 33bc164..836a55f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -108,11 +108,7 @@
* Dozing and receiving a notification (AOD notification.)
*/
private boolean mPulsing;
-
- /**
- * Distance in pixels between the top of the screen and the first view of the bouncer.
- */
- private int mBouncerTop;
+ private float mEmptyDragAmount;
/**
* Refreshes the dimension values.
@@ -131,9 +127,8 @@
}
public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
- float panelExpansion, int parentHeight,
- int keyguardStatusHeight, float dark, boolean secure, boolean pulsing,
- int bouncerTop) {
+ float panelExpansion, int parentHeight, int keyguardStatusHeight, float dark,
+ boolean secure, boolean pulsing, float emptyDragAmount) {
mMinTopMargin = minTopMargin + mContainerTopPadding;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
@@ -143,7 +138,7 @@
mDarkAmount = dark;
mCurrentlySecure = secure;
mPulsing = pulsing;
- mBouncerTop = bouncerTop;
+ mEmptyDragAmount = emptyDragAmount;
}
public void run(Result result) {
@@ -194,15 +189,14 @@
}
float clockYRegular = getExpandedClockPosition();
- boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
- float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
- mMinTopMargin : -mKeyguardStatusHeight;
+ float clockYBouncer = -mKeyguardStatusHeight;
// Move clock up while collapsing the shade
float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
- final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
+ float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion);
+ clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
- return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
+ return (int) (MathUtils.lerp(clockY, clockYDark, mDarkAmount) + mEmptyDragAmount);
}
/**
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 6bccf31..80f3506 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -236,7 +236,7 @@
try {
WindowManagerGlobal.getWindowManagerService()
- .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
+ .watchRotation(mRotationWatcher, getContext().getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -284,8 +284,8 @@
super.onViewCreated(view, savedInstanceState);
mNavigationBarView = (NavigationBarView) view;
- mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setComponents(mStatusBar.getPanel());
+ mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
if (savedInstanceState != null) {
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 c4efa94..e92656ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -60,7 +60,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
@@ -71,7 +70,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -140,6 +138,7 @@
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
private final ContextualButtonGroup mContextualButtonGroup;
private Configuration mConfiguration;
+ private Configuration mTmpLastConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
private RecentsOnboarding mRecentsOnboarding;
@@ -286,6 +285,7 @@
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mConfiguration = new Configuration();
+ mTmpLastConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
mScreenPinningNotify = new ScreenPinningNotify(mContext);
@@ -445,13 +445,13 @@
}
private void reloadNavIcons() {
- updateIcons(Configuration.EMPTY, mConfiguration);
+ updateIcons(Configuration.EMPTY);
}
- private void updateIcons(Configuration oldConfig, Configuration newConfig) {
- final boolean orientationChange = oldConfig.orientation != newConfig.orientation;
- final boolean densityChange = oldConfig.densityDpi != newConfig.densityDpi;
- final boolean dirChange = oldConfig.getLayoutDirection() != newConfig.getLayoutDirection();
+ private void updateIcons(Configuration oldConfig) {
+ final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
+ final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
+ final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
if (orientationChange || densityChange) {
mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked);
@@ -485,7 +485,7 @@
private void orientBackButton(KeyButtonDrawable drawable) {
final boolean useAltBack =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
- final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float degrees = useAltBack
? (isRtl ? 270 : -90)
: (isRtl ? 180 : 0);
@@ -946,26 +946,27 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- boolean uiCarModeChanged = updateCarMode(newConfig);
+ mTmpLastConfiguration.updateFrom(mConfiguration);
+ mConfiguration.updateFrom(newConfig);
+ boolean uiCarModeChanged = updateCarMode();
updateTaskSwitchHelper();
- updateIcons(mConfiguration, newConfig);
+ updateIcons(mTmpLastConfiguration);
updateRecentsIcon();
- mRecentsOnboarding.onConfigurationChanged(newConfig);
- if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi
- || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) {
+ mRecentsOnboarding.onConfigurationChanged(mConfiguration);
+ if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
+ || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
// If car mode or density changes, we need to reset the icons.
updateNavButtonIcons();
}
- mConfiguration.updateFrom(newConfig);
}
/**
* If the configuration changed, update the carmode and return that it was updated.
*/
- private boolean updateCarMode(Configuration newConfig) {
+ private boolean updateCarMode() {
boolean uiCarModeChanged = false;
- if (newConfig != null) {
- int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
+ if (mConfiguration != null) {
+ int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
if (isCarMode != mInCarMode) {
@@ -1100,10 +1101,11 @@
visibilityToString(getCurrentView().getVisibility()),
getCurrentView().getAlpha()));
- pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
+ pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f",
mDisabledFlags,
mVertical ? "true" : "false",
- getMenuButton().isVisible() ? "true" : "false"));
+ getMenuButton().isVisible() ? "true" : "false",
+ getLightTransitionsController().getCurrentDarkIntensity()));
dumpButton(pw, "back", getBackButton());
dumpButton(pw, "home", getHomeButton());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6d53cd3..e31bad65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -38,6 +38,7 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.PowerManager;
+import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@@ -104,6 +105,11 @@
private static final boolean DEBUG = false;
+ private static final boolean EXPAND_ON_WAKE_UP = SystemProperties.getBoolean(
+ "persist.sysui.expand_shade_on_wake_up", true);
+ private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
+ "persist.sysui.go_to_shade_on_wake_up", true);
+
/**
* Fling expanding QS.
*/
@@ -280,6 +286,12 @@
*/
private float mLinearDarkAmount;
+ /**
+ * State where the device isn't dozing anymore, but the lock screen isn't fully awake.
+ * The screen will be dimmed down with the shade collapsed.
+ */
+ private boolean mSemiAwake;
+
private float mDarkAmountTarget;
private boolean mPulsing;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -573,7 +585,7 @@
mInterpolatedDarkAmount,
mStatusBar.isKeyguardCurrentlySecure(),
mPulsing,
- mBouncerTop);
+ mEmptyDragAmount);
mClockPositionAlgorithm.run(mClockPositionResult);
PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
@@ -1235,6 +1247,12 @@
if (keyguardShowing) {
updateDozingVisibilities(false /* animate */);
}
+
+ // Expand notification shade if the device was is semi-awake state
+ if (mBarState == StatusBarState.SHADE && isSemiAwake()) {
+ mNotificationStackScroller.setDark(false /* dark */, false /* animated */,
+ null /* touchLocation */);
+ }
resetVerticalPanelPosition();
updateQsState();
}
@@ -2335,13 +2353,7 @@
}
public void setEmptyDragAmount(float amount) {
- float factor = 0.8f;
- if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
- factor = 0.4f;
- } else if (!mStatusBar.hasActiveNotifications()) {
- factor = 0.4f;
- }
- mEmptyDragAmount = amount * factor;
+ mEmptyDragAmount = amount * 0.2f;
positionClockAndNotifications();
}
@@ -2521,8 +2533,8 @@
@Override
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
super.setHeadsUpManager(headsUpManager);
- mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
- this);
+ mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
+ mNotificationStackScroller.getHeadsUpCallback(), this);
}
public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
@@ -2769,11 +2781,14 @@
mNotificationStackScroller.setAnimationsEnabled(!disabled);
}
- public void setDozing(boolean dozing, boolean animate,
- PointF wakeUpTouchLocation) {
- mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+ public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
+ boolean passiveInterrupted) {
if (dozing == mDozing) return;
mDozing = dozing;
+ mSemiAwake = !EXPAND_ON_WAKE_UP && !mDozing && passiveInterrupted;
+ if (!mSemiAwake) {
+ mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+ }
if (mBarState == StatusBarState.KEYGUARD
|| mBarState == StatusBarState.SHADE_LOCKED) {
@@ -2787,24 +2802,38 @@
} else {
mDarkAnimator.cancel();
}
+ if (mSemiAwake) {
+ setDarkAmount(0, 0);
+ }
}
mDarkAmountTarget = darkAmount;
- if (animate) {
- if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
- mDarkInterpolator = dozing
- ? Interpolators.FAST_OUT_SLOW_IN
- : Interpolators.TOUCH_RESPONSE_REVERSE;
+ if (!mSemiAwake) {
+ if (animate) {
+ startDarkAnimation();
+ } else {
+ setDarkAmount(darkAmount, darkAmount);
}
- mNotificationStackScroller.notifyDarkAnimationStart(dozing);
- mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
- mDarkAnimator.setInterpolator(Interpolators.LINEAR);
- mDarkAnimator.setDuration(mNotificationStackScroller.getDarkAnimationDuration(dozing));
- mDarkAnimator.start();
- } else {
- setDarkAmount(darkAmount, darkAmount);
}
}
+ public boolean isSemiAwake() {
+ return mSemiAwake;
+ }
+
+ private void startDarkAnimation() {
+ if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
+ mDarkInterpolator = mDozing
+ ? Interpolators.FAST_OUT_SLOW_IN
+ : Interpolators.TOUCH_RESPONSE_REVERSE;
+ }
+ mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
+ mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozing ? 1 : 0);
+ mDarkAnimator.setInterpolator(Interpolators.LINEAR);
+ mDarkAnimator.setDuration(
+ mNotificationStackScroller.getDarkAnimationDuration(mDozing));
+ mDarkAnimator.start();
+ }
+
private void setDarkAmount(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
@@ -2989,4 +3018,22 @@
mNotificationStackScroller.setScrimController(scrimController);
updateShowEmptyShadeView();
}
+
+ /**
+ * Whenever a user drags down on the empty area (pulling down the shade and clock) and lets go.
+ *
+ * @return {@code true} if dragging down should take the user to SHADE_LOCKED.
+ */
+ public boolean onDraggedDown() {
+ if (isSemiAwake()) {
+ mSemiAwake = false;
+ mNotificationStackScroller.setDark(false /* dark */, true /* animate */,
+ null /* touchLocation */);
+ startDarkAnimation();
+ mStatusBar.updateScrimController();
+
+ return WAKE_UP_TO_SHADE;
+ }
+ return true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e3a7b75..1bed26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -63,7 +63,7 @@
public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
Dumpable {
- private static final String TAG = "ScrimController";
+ static final String TAG = "ScrimController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
/**
@@ -96,6 +96,11 @@
*/
public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
/**
+ * A scrim varies its opacity based on a busyness factor, for example
+ * how many notifications are currently visible.
+ */
+ public static final float GRADIENT_SCRIM_DARK_KEYGUARD = 0.80f;
+ /**
* The most common scrim, the one under the keyguard.
*/
protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
@@ -361,7 +366,7 @@
mExpansionFraction = fraction;
final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
- || mState == ScrimState.KEYGUARD;
+ || mState == ScrimState.KEYGUARD || mState == ScrimState.DARK_KEYGUARD;
if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 085f7b6..ade063d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -49,6 +49,8 @@
// fade it out afterwards.
mBlankScreen = true;
}
+ } else if (previousState == ScrimState.KEYGUARD) {
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
} else {
mAnimationDuration = ScrimController.ANIMATION_DURATION;
}
@@ -59,8 +61,24 @@
@Override
public float getBehindAlpha(float busynessFactor) {
return MathUtils.map(0 /* start */, 1 /* stop */,
- mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
- busynessFactor);
+ mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+ busynessFactor);
+ }
+ },
+
+ /**
+ * On semi-awake lock screen.
+ */
+ DARK_KEYGUARD(7) {
+
+ @Override
+ public void prepare(ScrimState previousState) {
+ mBlankScreen = mDisplayRequiresBlanking && previousState != ScrimState.AOD;
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_DARK_KEYGUARD;
+ mCurrentInFrontAlpha = 0;
+ mCurrentInFrontTint = Color.BLACK;
+ mCurrentBehindTint = Color.BLACK;
}
},
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 56bef2e..56e5a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3617,7 +3617,8 @@
mDozeScrimController.setDozing(mDozing);
mKeyguardIndicationController.setDozing(mDozing);
- mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
+ mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation,
+ mDozeServiceHost.wasPassivelyInterrupted());
mNotificationLogger.setDozing(mDozing);
mGroupManager.setDozing(mDozing);
updateQsExpansionEnabled();
@@ -3756,9 +3757,6 @@
Trace.beginSection("StatusBar#updateKeyguardState");
if (mState == StatusBarState.KEYGUARD) {
mKeyguardIndicationController.setVisible(true);
- boolean dozingAnimated = mDozingRequested
- && DozeParameters.getInstance(mContext).shouldControlScreenOff();
- mNotificationPanel.resetViews(dozingAnimated);
if (mKeyguardUserSwitcher != null) {
mKeyguardUserSwitcher.setKeyguard(true,
mStatusBarStateController.fromShadeLocked());
@@ -3790,6 +3788,47 @@
}
@Override
+ public void onDozingChanged(boolean isDozing) {
+ Trace.beginSection("StatusBar#updateDozing");
+ mDozing = isDozing;
+
+ // Collapse the notification panel if open
+ boolean dozingAnimated = mDozingRequested
+ && DozeParameters.getInstance(mContext).shouldControlScreenOff();
+ mNotificationPanel.resetViews(dozingAnimated);
+
+ mKeyguardViewMediator.setAodShowing(mDozing);
+
+ //TODO: make these folks listeners of StatusBarStateController.onDozingChanged
+ mStatusBarWindowController.setDozing(mDozing);
+ mStatusBarKeyguardViewManager.setDozing(mDozing);
+ if (mAmbientIndicationContainer instanceof DozeReceiver) {
+ ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
+ }
+
+ mEntryManager.updateNotifications();
+ updateDozingState();
+ updateScrimController();
+ updateReportRejectedTouchVisibility();
+ Trace.endSection();
+ }
+
+ private void updateDozing() {
+ // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
+ boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
+ || mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ // When in wake-and-unlock we may not have received a change to mState
+ // but we still should not be dozing, manually set to false.
+ if (mBiometricUnlockController.getMode() ==
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
+ dozing = false;
+ }
+
+ mStatusBarStateController.setIsDozing(dozing);
+ }
+
+ @Override
public void onActivationReset(ActivatableNotificationView view) {
if (view == mNotificationPanel.getActivatedChild()) {
mNotificationPanel.setActivatedChild(null);
@@ -4165,7 +4204,6 @@
mAmbientPulseManager.releaseAllImmediately();
mVisualStabilityManager.setScreenOn(true);
mNotificationPanel.setTouchAndAnimationDisabled(false);
- mDozeServiceHost.stopDozing();
updateVisibleToUser();
updateIsKeyguard();
updateScrimController();
@@ -4341,34 +4379,6 @@
updateScrimController();
}
- private void updateDozing() {
- Trace.beginSection("StatusBar#updateDozing");
- // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
- || mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
- // When in wake-and-unlock we may not have received a change to mState
- // but we still should not be dozing, manually set to false.
- if (mBiometricUnlockController.getMode() ==
- mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
- dozing = false;
- }
- if (mDozing != dozing) {
- mDozing = dozing;
- mKeyguardViewMediator.setAodShowing(mDozing);
- mStatusBarWindowController.setDozing(mDozing);
- mStatusBarKeyguardViewManager.setDozing(mDozing);
- if (mAmbientIndicationContainer instanceof DozeReceiver) {
- ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
- }
- mEntryManager.updateNotifications();
- updateDozingState();
- updateScrimController();
- updateReportRejectedTouchVisibility();
- }
- Trace.endSection();
- }
-
@VisibleForTesting
void updateScrimController() {
Trace.beginSection("StatusBar#updateScrimController");
@@ -4391,6 +4401,9 @@
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+ if (mNotificationPanel.isSemiAwake()) {
+ state = ScrimState.DARK_KEYGUARD;
+ }
mScrimController.transitionTo(state);
} else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
|| launchingAffordanceWithPreview) {
@@ -4402,7 +4415,8 @@
} else if (mDozing) {
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !wakeAndUnlocking) {
- mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.transitionTo(mNotificationPanel.isSemiAwake()
+ ? ScrimState.DARK_KEYGUARD : ScrimState.KEYGUARD);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
@@ -4422,6 +4436,7 @@
private boolean mAnimateWakeup;
private boolean mAnimateScreenOff;
private boolean mIgnoreTouchWhilePulsing;
+ private boolean mPassivelyInterrupted;
@Override
public String toString() {
@@ -4505,6 +4520,11 @@
}
@Override
+ public void setPassiveInterrupt(boolean passiveInterrupt) {
+ mPassivelyInterrupted = passiveInterrupt;
+ }
+
+ @Override
public void onIgnoreTouchWhilePulsing(boolean ignore) {
if (ignore != mIgnoreTouchWhilePulsing) {
DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
@@ -4623,6 +4643,10 @@
public boolean shouldAnimateScreenOff() {
return mAnimateScreenOff;
}
+
+ public boolean wasPassivelyInterrupted() {
+ return mPassivelyInterrupted;
+ }
}
public boolean shouldIgnoreTouch() {
@@ -5267,8 +5291,7 @@
(Runnable saveImportance, StatusBarNotification sbn) -> {
// If the user has security enabled, show challenge if the setting is changed.
if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
- && (mState == StatusBarState.KEYGUARD ||
- mState == StatusBarState.SHADE_LOCKED)) {
+ && mKeyguardManager.isKeyguardLocked()) {
onLockedNotificationImportanceChange(() -> {
saveImportance.run();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 57c7e28..0d37b55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -173,9 +173,9 @@
}
if (state.dozing) {
- mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ mLpChanged.privateFlags |= LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
} else {
- mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 45b32c7..ad9b9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -58,6 +58,7 @@
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.Dependency;
+import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.DragDownHelper;
@@ -182,6 +183,11 @@
}
}
+ @VisibleForTesting
+ protected NotificationStackScrollLayout getStackScrollLayout() {
+ return mStackScrollLayout;
+ }
+
@Override
public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
@@ -215,8 +221,11 @@
public void setService(StatusBar service) {
mService = service;
- setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout,
- mStackScrollLayout));
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback();
+ DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback();
+ setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback,
+ dragDownCallback));
}
@VisibleForTesting
@@ -309,7 +318,7 @@
}
}
if (isDown) {
- mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+ getStackScrollLayout().closeControlsIfOutsideTouch(ev);
}
if (mService.isDozing()) {
mService.mDozeScrimController.extendPulse();
@@ -331,13 +340,14 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mService.isDozing() && !mStackScrollLayout.hasPulsingNotifications()) {
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) {
// Capture all touch events in always-on.
return true;
}
boolean intercept = false;
if (mNotificationPanel.isFullyExpanded()
- && mStackScrollLayout.getVisibility() == View.VISIBLE
+ && stackScrollLayout.getVisibility() == View.VISIBLE
&& mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& !mService.isBouncerShowing()
&& !mService.isDozing()) {
@@ -349,7 +359,7 @@
if (intercept) {
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
- mStackScrollLayout.onInterceptTouchEvent(cancellation);
+ stackScrollLayout.onInterceptTouchEvent(cancellation);
mNotificationPanel.onInterceptTouchEvent(cancellation);
cancellation.recycle();
}
@@ -391,8 +401,9 @@
}
public void cancelExpandHelper() {
- if (mStackScrollLayout != null) {
- mStackScrollLayout.cancelExpandHelper();
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ if (stackScrollLayout != null) {
+ stackScrollLayout.cancelExpandHelper();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index d477587..b4d24d16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -151,6 +153,7 @@
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
+ entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
protected void updatePinnedMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 24a28cb..70a3589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -461,6 +461,8 @@
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.handleBroadcast(intent);
}
+ mConfig = Config.readConfig(mContext);
+ mReceiverHandler.post(this::handleConfigurationChanged);
break;
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
@@ -1042,18 +1044,23 @@
config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
config.alwaysShowCdmaRssi =
res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
- config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
config.hspaDataDistinguishable =
res.getBoolean(R.bool.config_hspa_data_distinguishable);
- config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
CarrierConfigManager configMgr = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configMgr.getConfig();
+ // Handle specific carrier config values for the default data SIM
+ int defaultDataSubId = SubscriptionManager.from(context)
+ .getDefaultDataSubscriptionId();
+ PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId);
if (b != null) {
config.alwaysShowDataRatIcon = b.getBoolean(
CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
+ config.show4gForLte = b.getBoolean(
+ CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
+ config.hideLtePlus = b.getBoolean(
+ CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
}
return config;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
index af99236..e85dee8 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunablePadding.java
@@ -51,7 +51,9 @@
public void onTuningChanged(String key, String newValue) {
int dimen = mDefaultSize;
if (newValue != null) {
- dimen = (int) (Integer.parseInt(newValue) * mDensity);
+ try {
+ dimen = (int) (Integer.parseInt(newValue) * mDensity);
+ } catch (NumberFormatException ex) {}
}
int left = mView.isLayoutRtl() ? FLAG_END : FLAG_START;
int right = mView.isLayoutRtl() ? FLAG_START : FLAG_END;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 66d5ee1..4102e63 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -54,7 +54,7 @@
@Override
public void onCreate(Bundle icicle) {
Window window = getWindow();
- window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ window.addSystemFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
super.onCreate(icicle);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a97effd..e20e267 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -956,11 +956,13 @@
changed |= onVolumeChangedW(stream, 0);
} else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+ if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm;
if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
+ Util.ringerModeToString(rm));
changed = updateRingerModeExternalW(rm);
} else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+ if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm;
if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
+ Util.ringerModeToString(rm));
changed = updateRingerModeInternalW(rm);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4810b0b..798f8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,10 +135,6 @@
private final AccessibilityManagerWrapper mAccessibilityMgr;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
- private ColorStateList mActiveTint;
- private int mActiveAlpha;
- private ColorStateList mInactiveTint;
- private int mInactiveAlpha;
private boolean mShowing;
private boolean mShowA11yStream;
@@ -238,11 +234,6 @@
lp.gravity = ((FrameLayout.LayoutParams) mDialogView.getLayoutParams()).gravity;
mWindow.setAttributes(lp);
- mActiveTint = Utils.getColorAccent(mContext);
- mActiveAlpha = Color.alpha(mActiveTint.getDefaultColor());
- mInactiveTint = Utils.getColorAttr(mContext, android.R.attr.colorForeground);
- mInactiveAlpha = getAlphaAttr(android.R.attr.secondaryContentAlpha);
-
mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
mRinger = mDialog.findViewById(R.id.ringer);
if (mRinger != null) {
@@ -556,14 +547,15 @@
mHandler.removeMessages(H.SHOW);
mHandler.removeMessages(H.DISMISS);
rescheduleTimeoutH();
- mShowing = true;
if (mConfigChanged) {
- initDialog();
+ initDialog(); // resets mShowing to false
mConfigurableTexts.update();
mConfigChanged = false;
}
+
initSettingsH();
+ mShowing = true;
mDialog.show();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
@@ -941,8 +933,12 @@
row.slider.requestFocus();
}
boolean useActiveColoring = isActive && row.slider.isEnabled();
- final ColorStateList tint = useActiveColoring ? mActiveTint : mInactiveTint;
- final int alpha = useActiveColoring ? mActiveAlpha : mInactiveAlpha;
+ final ColorStateList tint = useActiveColoring
+ ? Utils.getColorAccent(mContext)
+ : Utils.getColorAttr(mContext, android.R.attr.colorForeground);
+ final int alpha = useActiveColoring
+ ? Color.alpha(tint.getDefaultColor())
+ : getAlphaAttr(android.R.attr.secondaryContentAlpha);
if (tint == row.cachedTint) return;
row.slider.setProgressTintList(tint);
row.slider.setThumbTintList(tint);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 2398fd3..6abd407 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -115,6 +115,11 @@
}
@Override
+ public void setPassiveInterrupt(boolean lightInterrupt) {
+
+ }
+
+ @Override
public void setDozeScreenBrightness(int value) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index bdd05c7..aae6d93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -28,6 +30,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.Context;
+import android.graphics.Color;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
@@ -45,6 +48,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)
@@ -54,9 +59,13 @@
private String mDisclosureWithOrganization;
- private DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
- private ViewGroup mIndicationArea = mock(ViewGroup.class);
- private KeyguardIndicationTextView mDisclosure = mock(KeyguardIndicationTextView.class);
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private ViewGroup mIndicationArea;
+ @Mock
+ private KeyguardIndicationTextView mDisclosure;
+ private KeyguardIndicationTextView mTextView;
private KeyguardIndicationController mController;
private WakeLockFake mWakeLock;
@@ -64,7 +73,9 @@
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mTextView = new KeyguardIndicationTextView(mContext);
mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
@@ -74,6 +85,7 @@
when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
.thenReturn(mDisclosure);
+ when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
mWakeLock = new WakeLockFake();
}
@@ -189,4 +201,17 @@
});
assertFalse("WakeLock expected: RELEASED, was: HELD", held[0]);
}
+
+ @Test
+ public void transientIndication_visibleWhenDozing() {
+ createController();
+
+ mController.setVisible(true);
+ mController.showTransientIndication("Test");
+ mController.setDozing(true);
+
+ assertThat(mTextView.getText()).isEqualTo("Test");
+ assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
+ assertThat(mTextView.getAlpha()).isEqualTo(1f);
+ }
}
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 edf29ac..aca1f90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,9 +31,9 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -67,16 +67,50 @@
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
+ /**
+ * Creates a generic row.
+ *
+ * @return a generic row with no special properties.
+ * @throws Exception
+ */
public ExpandableNotificationRow createRow() throws Exception {
return createRow(PKG, UID);
}
+ /**
+ * Create a row with the package and user id specified.
+ *
+ * @param pkg package
+ * @param uid user id
+ * @return a row with a notification using the package and user id
+ * @throws Exception
+ */
public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
}
+ /**
+ * Creates a row based off the notification given.
+ *
+ * @param notification the notification
+ * @return a row built off the notification
+ * @throws Exception
+ */
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
- return generateRow(notification, PKG, UID, false /* isGroupRow */);
+ return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
+ }
+
+ /**
+ * Create a row with the specified content views inflated in addition to the default.
+ *
+ * @param extraInflationFlags the flags corresponding to the additional content views that
+ * should be inflated
+ * @return a row with the specified content views inflated in addition to the default
+ * @throws Exception
+ */
+ public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
+ throws Exception {
+ return generateRow(createNotification(), PKG, UID, extraInflationFlags);
}
/**
@@ -122,34 +156,53 @@
boolean isGroupSummary,
@Nullable String groupKey)
throws Exception {
+ Notification notif = createNotification(isGroupSummary, groupKey);
+ return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
+ }
+
+ /**
+ * Creates a generic notification.
+ *
+ * @return a notification with no special properties
+ */
+ private Notification createNotification() {
+ return createNotification(false /* isGroupSummary */, null /* groupKey */);
+ }
+
+ /**
+ * Creates a notification with the given parameters.
+ *
+ * @param isGroupSummary whether the notification is a group summary
+ * @param groupKey the group key for the notification group used across notifications
+ * @return a notification that is in the group specified or standalone if unspecified
+ */
+ private Notification createNotification(boolean isGroupSummary,
+ @Nullable String groupKey) {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
R.layout.custom_view_dark))
.build();
- Notification.Builder notificationBuilder =
- new Notification.Builder(mContext, "channelId")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text")
- .setPublicVersion(publicVersion);
-
- // Group notification setup
+ Notification.Builder notificationBuilder = new Notification.Builder(mContext, "channelId")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text")
+ .setPublicVersion(publicVersion)
+ .setStyle(new Notification.BigTextStyle().bigText("Big Text"));
if (isGroupSummary) {
notificationBuilder.setGroupSummary(true);
}
if (!TextUtils.isEmpty(groupKey)) {
notificationBuilder.setGroup(groupKey);
}
-
- return generateRow(notificationBuilder.build(), pkg, uid, !TextUtils.isEmpty(groupKey));
+ return notificationBuilder.build();
}
private ExpandableNotificationRow generateRow(
Notification notification,
String pkg,
int uid,
- boolean isGroupRow)
+ @InflationFlag int extraInflationFlags)
throws Exception {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
@@ -179,8 +232,10 @@
entry.channel = new NotificationChannel(
notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
entry.channel.setBlockableSystem(true);
+ row.setEntry(entry);
+ row.getNotificationInflater().addInflationFlags(extraInflationFlags);
NotificationInflaterTest.runThenWaitForInflation(
- () -> row.updateNotification(entry),
+ () -> row.inflateViews(),
row.getNotificationInflater());
// This would be done as part of onAsyncInflationFinished, but we skip large amounts of
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 15c18e9..6b4ccc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -218,6 +218,9 @@
public void generateChildOrderChangedEvent() {}
@Override
+ public void onReset(ExpandableView view) {}
+
+ @Override
public int getContainerChildCount() {
return mRows.size();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 4e16b7f..f01ae7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -68,6 +68,7 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -134,8 +135,9 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
- super.onAsyncInflationFinished(entry);
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
+ super.onAsyncInflationFinished(entry, inflatedFlags);
mCountDownLatch.countDown();
}
@@ -428,7 +430,7 @@
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow).updateNotification(eq(mEntry));
+ verify(mRow).setEntry(eq(mEntry));
assertEquals(1, mEntry.smartActions.size());
assertEquals("action", mEntry.smartActions.get(0).title);
}
@@ -443,7 +445,7 @@
setSmartActions(mEntry.key, null);
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow, never()).updateNotification(eq(mEntry));
+ verify(mRow, never()).setEntry(eq(mEntry));
assertEquals(0, mEntry.smartActions.size());
}
@@ -457,7 +459,7 @@
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow, never()).updateNotification(eq(mEntry));
+ verify(mRow, never()).setEntry(eq(mEntry));
assertEquals(1, mEntry.smartActions.size());
assertEquals("action", mEntry.smartActions.get(0).title);
}
@@ -472,7 +474,7 @@
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow, never()).updateNotification(eq(mEntry));
+ verify(mRow, never()).setEntry(eq(mEntry));
assertEquals(1, mEntry.smartActions.size());
assertEquals("action", mEntry.smartActions.get(0).title);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 743b307..cfc7526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,8 +18,13 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -35,6 +40,7 @@
import android.app.NotificationChannel;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
@@ -134,6 +140,15 @@
}
@Test
+ public void testFreeContentViewWhenSafe() throws Exception {
+ ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
+
+ row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+
+ assertNull(row.getPrivateLayout().getHeadsUpChild());
+ }
+
+ @Test
public void testAboveShelfChangedListenerCalled() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index 81e79d1..150d933 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_ALL;
-
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -34,6 +37,7 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
@@ -82,7 +86,8 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
}
});
}
@@ -91,7 +96,7 @@
public void testIncreasedHeadsUpBeingUsed() {
mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
Notification.Builder builder = spy(mBuilder);
- mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+ mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
verify(builder).createHeadsUpContentView(true);
}
@@ -99,7 +104,7 @@
public void testIncreasedHeightBeingUsed() {
mNotificationInflater.setUsesIncreasedHeight(true);
Notification.Builder builder = spy(mBuilder);
- mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+ mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
verify(builder).createContentView(true);
}
@@ -111,14 +116,14 @@
}
@Test
- public void testInflationCallsOnlyRightMethod() throws Exception {
- mRow.getPrivateLayout().removeAllViews();
- mRow.getEntry().cachedBigContentView = null;
- runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
- FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
- assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
- assertTrue(mRow.getPrivateLayout().getChildAt(0)
- == mRow.getPrivateLayout().getExpandedChild());
+ public void testInflationOnlyInflatesSetFlags() throws Exception {
+ mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP,
+ true /* shouldInflate */);
+ runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+ mNotificationInflater);
+
+ assertNotNull(mRow.getPrivateLayout().getHeadsUpChild());
+ assertNull(mRow.getShowingLayout().getAmbientChild());
verify(mRow).onNotificationUpdated();
}
@@ -155,8 +160,9 @@
new NotificationInflater.InflationProgress();
result.packageContext = mContext;
CountDownLatch countDownLatch = new CountDownLatch(1);
- NotificationInflater.applyRemoteView(result, FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
- false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
+ NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+ new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
+ true /* isNewView */, new RemoteViews.OnClickHandler(),
new NotificationInflater.InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
@@ -166,10 +172,11 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
countDownLatch.countDown();
}
- }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
+ }, mRow.getPrivateLayout(), null, null, new HashMap<>(),
new NotificationInflater.ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -186,16 +193,19 @@
/* Cancelling requires us to be on the UI thread otherwise we might have a race */
@Test
- public void testSupersedesExistingTask() throws Exception {
+ public void testSupersedesExistingTask() {
+ mNotificationInflater.addInflationFlags(FLAG_CONTENT_VIEW_ALL);
mNotificationInflater.inflateNotificationViews();
+
+ // Trigger inflation of content and expanded only.
mNotificationInflater.setIsLowPriority(true);
mNotificationInflater.setIsChildInGroup(true);
+
InflationTask runningTask = mRow.getEntry().getRunningTask();
NotificationInflater.AsyncInflationTask asyncInflationTask =
(NotificationInflater.AsyncInflationTask) runningTask;
- Assert.assertSame("Successive inflations don't inherit the previous flags!",
- asyncInflationTask.getReInflateFlags(),
- NotificationInflater.FLAG_REINFLATE_ALL);
+ assertEquals("Successive inflations don't inherit the previous flags!",
+ asyncInflationTask.getReInflateFlags(), FLAG_CONTENT_VIEW_ALL);
runningTask.abort();
}
@@ -231,7 +241,8 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
if (expectingException) {
exceptionHolder.setException(new RuntimeException(
"Inflation finished even though there should be an error"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index f7a7e04..de26c70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -74,14 +74,14 @@
ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
verify(mWindowManager).updateViewLayout(any(), captor.capture());
int flag = captor.getValue().privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ & WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
assertThat(flag).isNotEqualTo(0);
reset(mWindowManager);
mStatusBarWindowController.setDozing(false);
verify(mWindowManager).updateViewLayout(any(), captor.capture());
flag = captor.getValue().privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ & WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
assertThat(flag).isEqualTo(0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 445a194..46335dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -17,11 +17,11 @@
package com.android.systemui.statusbar.phone;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.MotionEvent;
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import org.junit.Before;
import org.junit.Test;
@@ -43,11 +44,14 @@
private StatusBarWindowView mView;
private StatusBar mStatusBar;
private DragDownHelper mDragDownHelper;
+ private NotificationStackScrollLayout mStackScrollLayout;
@Before
public void setUp() {
mDependency.injectMockDependency(StatusBarStateController.class);
- mView = new StatusBarWindowView(getContext(), null);
+ mView = spy(new StatusBarWindowView(getContext(), null));
+ mStackScrollLayout = mock(NotificationStackScrollLayout.class);
+ when(mView.getStackScrollLayout()).thenReturn(mStackScrollLayout);
mStatusBar = mock(StatusBar.class);
mView.setService(mStatusBar);
mDragDownHelper = mock(DragDownHelper.class);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index d86de5d..da1fee3 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6564,6 +6564,11 @@
// OS: Q
MOBILE_ROAMING_DIALOG = 1583;
+ // OPEN: Settings > Display > Lock screen display > On lock screen
+ // CATEGORY: SETTINGS
+ // OS: Q
+ LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index b938f3b..17bf570 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -32,6 +32,8 @@
import android.text.TextUtils;
import android.util.MathUtils;
import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
import android.view.MagnificationSpec;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
@@ -40,6 +42,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerInternal;
@@ -55,7 +58,7 @@
* magnification region. If a value is out of bounds, it will be adjusted to guarantee these
* constraints.
*/
-public class MagnificationController implements Handler.Callback {
+public class MagnificationController {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "MagnificationController";
@@ -64,90 +67,553 @@
private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
- private static final int INVALID_ID = -1;
-
private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
- // Messages
- private static final int MSG_SEND_SPEC_TO_ANIMATION = 1;
- private static final int MSG_SCREEN_TURNED_OFF = 2;
- private static final int MSG_ON_MAGNIFIED_BOUNDS_CHANGED = 3;
- private static final int MSG_ON_RECTANGLE_ON_SCREEN_REQUESTED = 4;
- private static final int MSG_ON_USER_CONTEXT_CHANGED = 5;
-
private final Object mLock;
- /**
- * The current magnification spec. If an animation is running, this
- * reflects the end state.
- */
- private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
-
- private final Region mMagnificationRegion = Region.obtain();
- private final Rect mMagnificationBounds = new Rect();
-
- private final Rect mTempRect = new Rect();
- private final Rect mTempRect1 = new Rect();
-
private final AccessibilityManagerService mAms;
private final SettingsBridge mSettingsBridge;
private final ScreenStateObserver mScreenStateObserver;
- private final SpecAnimationBridge mSpecAnimationBridge;
-
- private final WindowManagerInternal.MagnificationCallbacks mWMCallbacks =
- new WindowManagerInternal.MagnificationCallbacks () {
- @Override
- public void onMagnificationRegionChanged(Region region) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = Region.obtain(region);
- mHandler.obtainMessage(MSG_ON_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
- }
-
- @Override
- public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
- final SomeArgs args = SomeArgs.obtain();
- args.argi1 = left;
- args.argi2 = top;
- args.argi3 = right;
- args.argi4 = bottom;
- mHandler.obtainMessage(MSG_ON_RECTANGLE_ON_SCREEN_REQUESTED, args)
- .sendToTarget();
- }
-
- @Override
- public void onRotationChanged(int rotation) {
- // Treat as context change and reset
- mHandler.sendEmptyMessage(MSG_ON_USER_CONTEXT_CHANGED);
- }
-
- @Override
- public void onUserContextChanged() {
- mHandler.sendEmptyMessage(MSG_ON_USER_CONTEXT_CHANGED);
- }
- };
-
private int mUserId;
private final long mMainThreadId;
private Handler mHandler;
- private int mIdOfLastServiceToMagnify = INVALID_ID;
-
private final WindowManagerInternal mWindowManager;
- // Flag indicating that we are registered with window manager.
- @VisibleForTesting boolean mRegistered;
+ private final DisplayMagnification mDisplay;
- private boolean mUnregisterPending;
+ /**
+ * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
+ * magnification information per display.
+ */
+ private final class DisplayMagnification implements
+ WindowManagerInternal.MagnificationCallbacks {
+ /**
+ * The current magnification spec. If an animation is running, this
+ * reflects the end state.
+ */
+ private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
+
+ private final Region mMagnificationRegion = Region.obtain();
+ private final Rect mMagnificationBounds = new Rect();
+
+ private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+
+ private final SpecAnimationBridge mSpecAnimationBridge;
+
+ // Flag indicating that we are registered with window manager.
+ private boolean mRegistered;
+ private boolean mUnregisterPending;
+
+ private final int mDisplayId;
+
+ private static final int INVALID_ID = -1;
+ private int mIdOfLastServiceToMagnify = INVALID_ID;
+
+
+ DisplayMagnification(int displayId, SpecAnimationBridge specAnimation) {
+ mDisplayId = displayId;
+ mSpecAnimationBridge = specAnimation;
+ }
+
+ void register() {
+ synchronized (mLock) {
+ if (!mRegistered) {
+ mWindowManager.setMagnificationCallbacks(this);
+ mSpecAnimationBridge.setEnabled(true);
+ // Obtain initial state.
+ mWindowManager.getMagnificationRegion(mMagnificationRegion);
+ mMagnificationRegion.getBounds(mMagnificationBounds);
+ mRegistered = true;
+ }
+ }
+ }
+
+ void unregister() {
+ synchronized (mLock) {
+ if (!isMagnifying()) {
+ unregisterInternalLocked();
+ } else {
+ mUnregisterPending = true;
+ reset(true);
+ }
+ }
+ }
+
+ boolean isRegisteredLocked() {
+ return mRegistered;
+ }
+
+
+ float getScale() {
+ return mCurrentMagnificationSpec.scale;
+ }
+
+ float getOffsetX() {
+ return mCurrentMagnificationSpec.offsetX;
+ }
+
+ float getCenterX() {
+ synchronized (mLock) {
+ return (mMagnificationBounds.width() / 2.0f
+ + mMagnificationBounds.left - getOffsetX()) / getScale();
+ }
+ }
+
+ float getCenterY() {
+ synchronized (mLock) {
+ return (mMagnificationBounds.height() / 2.0f
+ + mMagnificationBounds.top - getOffsetY()) / getScale();
+ }
+ }
+
+ /**
+ * Returns the scale currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the scale currently used by the window manager
+ */
+ float getSentScale() {
+ return mSpecAnimationBridge.mSentMagnificationSpec.scale;
+ }
+
+ /**
+ * Returns the X offset currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the X offset currently used by the window manager
+ */
+ float getSentOffsetX() {
+ return mSpecAnimationBridge.mSentMagnificationSpec.offsetX;
+ }
+
+ /**
+ * Returns the Y offset currently used by the window manager. If an
+ * animation is in progress, this reflects the current state of the
+ * animation.
+ *
+ * @return the Y offset currently used by the window manager
+ */
+ float getSentOffsetY() {
+ return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
+ }
+
+ boolean resetIfNeeded(boolean animate) {
+ synchronized (mLock) {
+ if (isMagnifying()) {
+ reset(animate);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ float getOffsetY() {
+ return mCurrentMagnificationSpec.offsetY;
+ }
+
+ boolean isMagnifying() {
+ return mCurrentMagnificationSpec.scale > 1.0f;
+ }
+
+ void unregisterInternalLocked() {
+ if (mRegistered) {
+ mSpecAnimationBridge.setEnabled(false);
+ mWindowManager.setMagnificationCallbacks(null);
+ mMagnificationRegion.setEmpty();
+
+ mRegistered = false;
+ }
+ mUnregisterPending = false;
+ }
+
+
+ @Override
+ public void onMagnificationRegionChanged(Region magnificationRegion) {
+ final Message m = PooledLambda.obtainMessage(
+ DisplayMagnification.this::updateMagnificationRegion,
+ Region.obtain(magnificationRegion));
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
+ final Message m = PooledLambda.obtainMessage(
+ DisplayMagnification.this::requestRectangleOnScreen, left, top, right, bottom);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ public void onRotationChanged(int rotation) {
+ // Treat as context change and reset
+ final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
+ true);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ public void onUserContextChanged() {
+ final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
+ true);
+ mHandler.sendMessage(m);
+ }
+
+ /**
+ * Update our copy of the current magnification region
+ *
+ * @param magnified the magnified region
+ */
+ void updateMagnificationRegion(Region magnified) {
+ synchronized (mLock) {
+ if (!mRegistered) {
+ // Don't update if we've unregistered
+ return;
+ }
+ if (!mMagnificationRegion.equals(magnified)) {
+ mMagnificationRegion.set(magnified);
+ mMagnificationRegion.getBounds(mMagnificationBounds);
+ // It's possible that our magnification spec is invalid with the new bounds.
+ // Adjust the current spec's offsets if necessary.
+ if (updateCurrentSpecWithOffsetsLocked(
+ mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) {
+ sendSpecToAnimation(mCurrentMagnificationSpec, false);
+ }
+ onMagnificationChangedLocked();
+ }
+ magnified.recycle();
+ }
+ }
+
+ void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG,
+ "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
+ }
+ if (Thread.currentThread().getId() == mMainThreadId) {
+ mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
+ } else {
+ final Message m = PooledLambda.obtainMessage(
+ this.mSpecAnimationBridge::updateSentSpecMainThread, spec, animate);
+ mHandler.sendMessage(m);
+ }
+ }
+
+ /**
+ * Get the ID of the last service that changed the magnification spec.
+ *
+ * @return The id
+ */
+ int getIdOfLastServiceToMagnify() {
+ return mIdOfLastServiceToMagnify;
+ }
+
+ void onMagnificationChangedLocked() {
+ mAms.notifyMagnificationChanged(mMagnificationRegion,
+ getScale(), getCenterX(), getCenterY());
+ if (mUnregisterPending && !isMagnifying()) {
+ unregisterInternalLocked();
+ }
+ }
+
+ boolean magnificationRegionContains(float x, float y) {
+ synchronized (mLock) {
+ return mMagnificationRegion.contains((int) x, (int) y);
+
+ }
+ }
+
+ void getMagnificationBounds(@NonNull Rect outBounds) {
+ synchronized (mLock) {
+ outBounds.set(mMagnificationBounds);
+ }
+ }
+
+ void getMagnificationRegion(@NonNull Region outRegion) {
+ synchronized (mLock) {
+ outRegion.set(mMagnificationRegion);
+ }
+ }
+
+ void requestRectangleOnScreen(int left, int top, int right, int bottom) {
+ synchronized (mLock) {
+ final Rect magnifiedFrame = mTempRect;
+ getMagnificationBounds(magnifiedFrame);
+ if (!magnifiedFrame.intersects(left, top, right, bottom)) {
+ return;
+ }
+
+ final Rect magnifFrameInScreenCoords = mTempRect1;
+ getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords);
+
+ final float scrollX;
+ final float scrollY;
+ if (right - left > magnifFrameInScreenCoords.width()) {
+ final int direction = TextUtils
+ .getLayoutDirectionFromLocale(Locale.getDefault());
+ if (direction == View.LAYOUT_DIRECTION_LTR) {
+ scrollX = left - magnifFrameInScreenCoords.left;
+ } else {
+ scrollX = right - magnifFrameInScreenCoords.right;
+ }
+ } else if (left < magnifFrameInScreenCoords.left) {
+ scrollX = left - magnifFrameInScreenCoords.left;
+ } else if (right > magnifFrameInScreenCoords.right) {
+ scrollX = right - magnifFrameInScreenCoords.right;
+ } else {
+ scrollX = 0;
+ }
+
+ if (bottom - top > magnifFrameInScreenCoords.height()) {
+ scrollY = top - magnifFrameInScreenCoords.top;
+ } else if (top < magnifFrameInScreenCoords.top) {
+ scrollY = top - magnifFrameInScreenCoords.top;
+ } else if (bottom > magnifFrameInScreenCoords.bottom) {
+ scrollY = bottom - magnifFrameInScreenCoords.bottom;
+ } else {
+ scrollY = 0;
+ }
+
+ final float scale = getScale();
+ offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID);
+ }
+ }
+
+ void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
+ final float scale = getSentScale();
+ final float offsetX = getSentOffsetX();
+ final float offsetY = getSentOffsetY();
+ getMagnificationBounds(outFrame);
+ outFrame.offset((int) -offsetX, (int) -offsetY);
+ outFrame.scale(1.0f / scale);
+ }
+
+ /**
+ * Resets magnification if last magnifying service is disabled.
+ *
+ * @param connectionId the connection ID be disabled.
+ * @return {@code true} on success, {@code false} on failure
+ */
+ boolean resetIfNeeded(int connectionId) {
+ if (mIdOfLastServiceToMagnify == connectionId) {
+ return resetIfNeeded(true /*animate*/);
+ }
+ return false;
+ }
+
+ void setForceShowMagnifiableBounds(boolean show) {
+ if (mRegistered) {
+ mWindowManager.setForceShowMagnifiableBounds(show);
+ }
+ }
+
+ boolean reset(boolean animate) {
+ synchronized (mLock) {
+ if (!mRegistered) {
+ return false;
+ }
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final boolean changed = !spec.isNop();
+ if (changed) {
+ spec.clear();
+ onMagnificationChangedLocked();
+ }
+ mIdOfLastServiceToMagnify = INVALID_ID;
+ sendSpecToAnimation(spec, animate);
+ return changed;
+ }
+ }
+
+
+ boolean setScale(float scale, float pivotX, float pivotY,
+ boolean animate, int id) {
+
+ synchronized (mLock) {
+ if (!mRegistered) {
+ return false;
+ }
+ // Constrain scale immediately for use in the pivot calculations.
+ scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+
+ final Rect viewport = mTempRect;
+ mMagnificationRegion.getBounds(viewport);
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.scale;
+ final float oldCenterX
+ = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
+ final float oldCenterY
+ = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
+ final float normPivotX = (pivotX - spec.offsetX) / oldScale;
+ final float normPivotY = (pivotY - spec.offsetY) / oldScale;
+ final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+ final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+ final float centerX = normPivotX + offsetX;
+ final float centerY = normPivotY + offsetY;
+ mIdOfLastServiceToMagnify = id;
+
+ return setScaleAndCenter(scale, centerX, centerY, animate, id);
+ }
+ }
+
+ boolean setScaleAndCenter(float scale, float centerX, float centerY,
+ boolean animate, int id) {
+
+ synchronized (mLock) {
+ if (!mRegistered) {
+ return false;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG,
+ "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
+ + ", centerY = " + centerY + ", animate = " + animate
+ + ", id = " + id
+ + ")");
+ }
+ final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
+ sendSpecToAnimation(mCurrentMagnificationSpec, animate);
+ if (isMagnifying() && (id != INVALID_ID)) {
+ mIdOfLastServiceToMagnify = id;
+ }
+ return changed;
+ }
+ }
+
+ /**
+ * Updates the current magnification spec.
+ *
+ * @param scale the magnification scale
+ * @param centerX the unscaled, screen-relative X coordinate of the center
+ * of the viewport, or {@link Float#NaN} to leave unchanged
+ * @param centerY the unscaled, screen-relative Y coordinate of the center
+ * of the viewport, or {@link Float#NaN} to leave unchanged
+ * @return {@code true} if the magnification spec changed or {@code false}
+ * otherwise
+ */
+ boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
+ // Handle defaults.
+ if (Float.isNaN(centerX)) {
+ centerX = getCenterX();
+ }
+ if (Float.isNaN(centerY)) {
+ centerY = getCenterY();
+ }
+ if (Float.isNaN(scale)) {
+ scale = getScale();
+ }
+
+ // Compute changes.
+ boolean changed = false;
+
+ final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
+ mCurrentMagnificationSpec.scale = normScale;
+ changed = true;
+ }
+
+ final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f
+ + mMagnificationBounds.left - centerX * normScale;
+ final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f
+ + mMagnificationBounds.top - centerY * normScale;
+ changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
+
+ if (changed) {
+ onMagnificationChangedLocked();
+ }
+
+ return changed;
+ }
+
+ void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
+ synchronized (mLock) {
+ if (!mRegistered) {
+ return;
+ }
+
+ final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
+ final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
+ if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
+ onMagnificationChangedLocked();
+ }
+ if (id != INVALID_ID) {
+ mIdOfLastServiceToMagnify = id;
+ }
+ sendSpecToAnimation(mCurrentMagnificationSpec, false);
+ }
+ }
+
+ boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG,
+ "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
+ + ", nonNormOffsetY = " + nonNormOffsetY + ")");
+ }
+ boolean changed = false;
+ final float offsetX = MathUtils.constrain(
+ nonNormOffsetX, getMinOffsetXLocked(), getMaxOffsetXLocked());
+ if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
+ mCurrentMagnificationSpec.offsetX = offsetX;
+ changed = true;
+ }
+ final float offsetY = MathUtils.constrain(
+ nonNormOffsetY, getMinOffsetYLocked(), getMaxOffsetYLocked());
+ if (Float.compare(mCurrentMagnificationSpec.offsetY, offsetY) != 0) {
+ mCurrentMagnificationSpec.offsetY = offsetY;
+ changed = true;
+ }
+ return changed;
+ }
+
+ float getMinOffsetXLocked() {
+ final float viewportWidth = mMagnificationBounds.width();
+ final float viewportLeft = mMagnificationBounds.left;
+ return (viewportLeft + viewportWidth) -
+ (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
+ }
+
+ float getMaxOffsetXLocked() {
+ return mMagnificationBounds.left -
+ mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
+ }
+
+ float getMinOffsetYLocked() {
+ final float viewportHeight = mMagnificationBounds.height();
+ final float viewportTop = mMagnificationBounds.top;
+ return (viewportTop + viewportHeight) -
+ (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
+ }
+
+ float getMaxOffsetYLocked() {
+ return mMagnificationBounds.top -
+ mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
+ }
+
+ @Override
+ public String toString() {
+ return "DisplayMagnification{" +
+ "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
+ ", mMagnificationRegion=" + mMagnificationRegion +
+ ", mMagnificationBounds=" + mMagnificationBounds +
+ ", mDisplayId=" + mDisplayId +
+ ", mUserId=" + mUserId +
+ ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
+ ", mRegistered=" + mRegistered +
+ ", mUnregisterPending=" + mUnregisterPending +
+ '}';
+ }
+
+ }
public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
this(context, ams, lock, null, LocalServices.getService(WindowManagerInternal.class),
new ValueAnimator(), new SettingsBridge(context.getContentResolver()));
- mHandler = new Handler(context.getMainLooper(), this);
+ mHandler = new Handler(context.getMainLooper());
}
public MagnificationController(
@@ -164,9 +630,10 @@
mAms = ams;
mScreenStateObserver = new ScreenStateObserver(context, this);
mLock = lock;
- mSpecAnimationBridge = new SpecAnimationBridge(
- context, mLock, mWindowManager, valueAnimator);
mSettingsBridge = settingsBridge;
+ //TODO (multidisplay): Magnification is supported only for the default display.
+ mDisplay = new DisplayMagnification(Display.DEFAULT_DISPLAY,
+ new SpecAnimationBridge(context, mLock, mWindowManager, valueAnimator));
}
/**
@@ -178,16 +645,9 @@
*/
public void register() {
synchronized (mLock) {
- if (!mRegistered) {
- mScreenStateObserver.register();
- mWindowManager.setMagnificationCallbacks(mWMCallbacks);
- mSpecAnimationBridge.setEnabled(true);
- // Obtain initial state.
- mWindowManager.getMagnificationRegion(mMagnificationRegion);
- mMagnificationRegion.getBounds(mMagnificationBounds);
- mRegistered = true;
- }
+ mScreenStateObserver.register();
}
+ mDisplay.register();
}
/**
@@ -196,33 +656,18 @@
*/
public void unregister() {
synchronized (mLock) {
- if (!isMagnifying()) {
- unregisterInternalLocked();
- } else {
- mUnregisterPending = true;
- resetLocked(true);
- }
+ mScreenStateObserver.unregister();
}
+ mDisplay.unregister();
}
-
+
/**
* Check if we are registered. Note that we may be planning to unregister at any moment.
*
* @return {@code true} if the controller is registered. {@code false} otherwise.
*/
public boolean isRegisteredLocked() {
- return mRegistered;
- }
-
- private void unregisterInternalLocked() {
- if (mRegistered) {
- mSpecAnimationBridge.setEnabled(false);
- mScreenStateObserver.unregister();
- mWindowManager.setMagnificationCallbacks(null);
- mMagnificationRegion.setEmpty();
- mRegistered = false;
- }
- mUnregisterPending = false;
+ return mDisplay.isRegisteredLocked();
}
/**
@@ -230,32 +675,7 @@
* is > 1, {@code false} otherwise
*/
public boolean isMagnifying() {
- return mCurrentMagnificationSpec.scale > 1.0f;
- }
-
- /**
- * Update our copy of the current magnification region
- *
- * @param magnified the magnified region
- */
- private void onMagnificationRegionChanged(Region magnified) {
- synchronized (mLock) {
- if (!mRegistered) {
- // Don't update if we've unregistered
- return;
- }
- if (!mMagnificationRegion.equals(magnified)) {
- mMagnificationRegion.set(magnified);
- mMagnificationRegion.getBounds(mMagnificationBounds);
- // It's possible that our magnification spec is invalid with the new bounds.
- // Adjust the current spec's offsets if necessary.
- if (updateCurrentSpecWithOffsetsLocked(
- mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) {
- sendSpecToAnimation(mCurrentMagnificationSpec, false);
- }
- onMagnificationChangedLocked();
- }
- }
+ return mDisplay.isMagnifying();
}
/**
@@ -268,9 +688,8 @@
* magnified region, or {@code false} otherwise
*/
public boolean magnificationRegionContains(float x, float y) {
- synchronized (mLock) {
- return mMagnificationRegion.contains((int) x, (int) y);
- }
+ return mDisplay.magnificationRegionContains(x, y);
+
}
/**
@@ -282,9 +701,7 @@
* region
*/
public void getMagnificationBounds(@NonNull Rect outBounds) {
- synchronized (mLock) {
- outBounds.set(mMagnificationBounds);
- }
+ mDisplay.getMagnificationBounds(outBounds);
}
/**
@@ -295,9 +712,7 @@
* @param outRegion the region to populate
*/
public void getMagnificationRegion(@NonNull Region outRegion) {
- synchronized (mLock) {
- outRegion.set(mMagnificationRegion);
- }
+ mDisplay.getMagnificationRegion(outRegion);
}
/**
@@ -307,7 +722,7 @@
* @return the scale
*/
public float getScale() {
- return mCurrentMagnificationSpec.scale;
+ return mDisplay.getScale();
}
/**
@@ -317,7 +732,7 @@
* @return the X offset
*/
public float getOffsetX() {
- return mCurrentMagnificationSpec.offsetX;
+ return mDisplay.getOffsetX();
}
@@ -328,10 +743,7 @@
* @return the X coordinate
*/
public float getCenterX() {
- synchronized (mLock) {
- return (mMagnificationBounds.width() / 2.0f
- + mMagnificationBounds.left - getOffsetX()) / getScale();
- }
+ return mDisplay.getCenterX();
}
/**
@@ -341,7 +753,7 @@
* @return the Y offset
*/
public float getOffsetY() {
- return mCurrentMagnificationSpec.offsetY;
+ return mDisplay.getOffsetY();
}
/**
@@ -351,43 +763,7 @@
* @return the Y coordinate
*/
public float getCenterY() {
- synchronized (mLock) {
- return (mMagnificationBounds.height() / 2.0f
- + mMagnificationBounds.top - getOffsetY()) / getScale();
- }
- }
-
- /**
- * Returns the scale currently used by the window manager. If an
- * animation is in progress, this reflects the current state of the
- * animation.
- *
- * @return the scale currently used by the window manager
- */
- private float getSentScale() {
- return mSpecAnimationBridge.mSentMagnificationSpec.scale;
- }
-
- /**
- * Returns the X offset currently used by the window manager. If an
- * animation is in progress, this reflects the current state of the
- * animation.
- *
- * @return the X offset currently used by the window manager
- */
- private float getSentOffsetX() {
- return mSpecAnimationBridge.mSentMagnificationSpec.offsetX;
- }
-
- /**
- * Returns the Y offset currently used by the window manager. If an
- * animation is in progress, this reflects the current state of the
- * animation.
- *
- * @return the Y offset currently used by the window manager
- */
- private float getSentOffsetY() {
- return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
+ return mDisplay.getCenterY();
}
/**
@@ -400,24 +776,9 @@
* the spec did not change
*/
public boolean reset(boolean animate) {
- synchronized (mLock) {
- return resetLocked(animate);
- }
- }
- private boolean resetLocked(boolean animate) {
- if (!mRegistered) {
- return false;
- }
- final MagnificationSpec spec = mCurrentMagnificationSpec;
- final boolean changed = !spec.isNop();
- if (changed) {
- spec.clear();
- onMagnificationChangedLocked();
- }
- mIdOfLastServiceToMagnify = INVALID_ID;
- sendSpecToAnimation(spec, animate);
- return changed;
+ return mDisplay.reset(animate);
+
}
/**
@@ -435,30 +796,8 @@
* the spec did not change
*/
public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) {
- synchronized (mLock) {
- if (!mRegistered) {
- return false;
- }
- // Constrain scale immediately for use in the pivot calculations.
- scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-
- final Rect viewport = mTempRect;
- mMagnificationRegion.getBounds(viewport);
- final MagnificationSpec spec = mCurrentMagnificationSpec;
- final float oldScale = spec.scale;
- final float oldCenterX
- = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
- final float oldCenterY
- = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
- final float normPivotX = (pivotX - spec.offsetX) / oldScale;
- final float normPivotY = (pivotY - spec.offsetY) / oldScale;
- final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
- final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
- final float centerX = normPivotX + offsetX;
- final float centerY = normPivotY + offsetY;
- mIdOfLastServiceToMagnify = id;
- return setScaleAndCenterLocked(scale, centerX, centerY, animate, id);
- }
+ return mDisplay.
+ setScale(scale, pivotX, pivotY, animate, id);
}
/**
@@ -471,17 +810,13 @@
* center
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
- * @param id the ID of the service requesting the change
+ * @param id the ID of the service requesting the change
* @return {@code true} if the magnification spec changed, {@code false} if
- * the spec did not change
+ * the spec did not change
*/
public boolean setCenter(float centerX, float centerY, boolean animate, int id) {
- synchronized (mLock) {
- if (!mRegistered) {
- return false;
- }
- return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate, id);
- }
+ return mDisplay.
+ setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
}
/**
@@ -502,28 +837,8 @@
*/
public boolean setScaleAndCenter(
float scale, float centerX, float centerY, boolean animate, int id) {
- synchronized (mLock) {
- if (!mRegistered) {
- return false;
- }
- return setScaleAndCenterLocked(scale, centerX, centerY, animate, id);
- }
- }
-
- private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY,
- boolean animate, int id) {
- if (DEBUG) {
- Slog.i(LOG_TAG,
- "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
- + ", centerY = " + centerY + ", animate = " + animate + ", id = " + id
- + ")");
- }
- final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
- sendSpecToAnimation(mCurrentMagnificationSpec, animate);
- if (isMagnifying() && (id != INVALID_ID)) {
- mIdOfLastServiceToMagnify = id;
- }
- return changed;
+ return mDisplay.
+ setScaleAndCenter(scale, centerX, centerY, animate, id);
}
/**
@@ -531,27 +846,14 @@
* opposite direction as the offsets passed in here.
*
* @param offsetX the amount in pixels to offset the region in the X direction, in current
- * screen pixels.
+ * screen pixels.
* @param offsetY the amount in pixels to offset the region in the Y direction, in current
- * screen pixels.
- * @param id the ID of the service requesting the change
+ * screen pixels.
+ * @param id the ID of the service requesting the change
*/
public void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
- synchronized (mLock) {
- if (!mRegistered) {
- return;
- }
-
- final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
- final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
- if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
- onMagnificationChangedLocked();
- }
- if (id != INVALID_ID) {
- mIdOfLastServiceToMagnify = id;
- }
- sendSpecToAnimation(mCurrentMagnificationSpec, false);
- }
+ mDisplay.offsetMagnifiedRegion(offsetX, offsetY,
+ id);
}
/**
@@ -560,28 +862,26 @@
* @return The id
*/
public int getIdOfLastServiceToMagnify() {
- return mIdOfLastServiceToMagnify;
- }
-
- private void onMagnificationChangedLocked() {
- mAms.notifyMagnificationChanged(mMagnificationRegion,
- getScale(), getCenterX(), getCenterY());
- if (mUnregisterPending && !isMagnifying()) {
- unregisterInternalLocked();
- }
+ return mDisplay.getIdOfLastServiceToMagnify();
}
/**
* Persists the current magnification scale to the current user's settings.
*/
public void persistScale() {
- final float scale = mCurrentMagnificationSpec.scale;
+ persistScale(Display.DEFAULT_DISPLAY);
+ }
+ /**
+ * Persists the current magnification scale to the current user's settings.
+ */
+ public void persistScale(int displayId) {
+ final float scale = mDisplay.getScale();
final int userId = mUserId;
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- mSettingsBridge.putMagnificationScale(scale, userId);
+ mSettingsBridge.putMagnificationScale(scale, displayId, userId);
return null;
}
}.execute();
@@ -595,98 +895,7 @@
* scale if none is available
*/
public float getPersistedScale() {
- return mSettingsBridge.getMagnificationScale(mUserId);
- }
-
- /**
- * Updates the current magnification spec.
- *
- * @param scale the magnification scale
- * @param centerX the unscaled, screen-relative X coordinate of the center
- * of the viewport, or {@link Float#NaN} to leave unchanged
- * @param centerY the unscaled, screen-relative Y coordinate of the center
- * of the viewport, or {@link Float#NaN} to leave unchanged
- * @return {@code true} if the magnification spec changed or {@code false}
- * otherwise
- */
- private boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
- // Handle defaults.
- if (Float.isNaN(centerX)) {
- centerX = getCenterX();
- }
- if (Float.isNaN(centerY)) {
- centerY = getCenterY();
- }
- if (Float.isNaN(scale)) {
- scale = getScale();
- }
-
- // Compute changes.
- boolean changed = false;
-
- final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
- if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
- mCurrentMagnificationSpec.scale = normScale;
- changed = true;
- }
-
- final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f
- + mMagnificationBounds.left - centerX * normScale;
- final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f
- + mMagnificationBounds.top - centerY * normScale;
- changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
-
- if (changed) {
- onMagnificationChangedLocked();
- }
-
- return changed;
- }
-
- private boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
- if (DEBUG) {
- Slog.i(LOG_TAG,
- "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
- + ", nonNormOffsetY = " + nonNormOffsetY + ")");
- }
- boolean changed = false;
- final float offsetX = MathUtils.constrain(
- nonNormOffsetX, getMinOffsetXLocked(), getMaxOffsetXLocked());
- if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
- mCurrentMagnificationSpec.offsetX = offsetX;
- changed = true;
- }
- final float offsetY = MathUtils.constrain(
- nonNormOffsetY, getMinOffsetYLocked(), getMaxOffsetYLocked());
- if (Float.compare(mCurrentMagnificationSpec.offsetY, offsetY) != 0) {
- mCurrentMagnificationSpec.offsetY = offsetY;
- changed = true;
- }
- return changed;
- }
-
- private float getMinOffsetXLocked() {
- final float viewportWidth = mMagnificationBounds.width();
- final float viewportLeft = mMagnificationBounds.left;
- return (viewportLeft + viewportWidth) -
- (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
- }
-
- private float getMaxOffsetXLocked() {
- return mMagnificationBounds.left -
- mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
- }
-
- private float getMinOffsetYLocked() {
- final float viewportHeight = mMagnificationBounds.height();
- final float viewportTop = mMagnificationBounds.top;
- return (viewportTop + viewportHeight) -
- (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
- }
-
- private float getMaxOffsetYLocked() {
- return mMagnificationBounds.top -
- mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
+ return mSettingsBridge.getMagnificationScale(Display.DEFAULT_DISPLAY, mUserId);
}
/**
@@ -706,20 +915,14 @@
}
}
- /**
+ /**
* Resets magnification if magnification and auto-update are both enabled.
*
* @param animate whether the animate the transition
* @return whether was {@link #isMagnifying magnifying}
*/
- boolean resetIfNeeded(boolean animate) {
- synchronized (mLock) {
- if (isMagnifying()) {
- reset(animate);
- return true;
- }
- return false;
- }
+ public boolean resetIfNeeded(boolean animate) {
+ return mDisplay.resetIfNeeded(animate);
}
/**
@@ -728,132 +931,23 @@
* @param connectionId the connection ID be disabled.
* @return {@code true} on success, {@code false} on failure
*/
- boolean resetIfNeeded(int connectionId) {
- if (mIdOfLastServiceToMagnify == connectionId) {
- return resetIfNeeded(true /*animate*/);
- }
- return false;
+ public boolean resetIfNeeded(int connectionId) {
+ return mDisplay.resetIfNeeded(connectionId);
}
void setForceShowMagnifiableBounds(boolean show) {
- if (mRegistered) {
- mWindowManager.setForceShowMagnifiableBounds(show);
- }
- }
-
- private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
- final float scale = getSentScale();
- final float offsetX = getSentOffsetX();
- final float offsetY = getSentOffsetY();
- getMagnificationBounds(outFrame);
- outFrame.offset((int) -offsetX, (int) -offsetY);
- outFrame.scale(1.0f / scale);
- }
-
- private void requestRectangleOnScreen(int left, int top, int right, int bottom) {
- synchronized (mLock) {
- final Rect magnifiedFrame = mTempRect;
- getMagnificationBounds(magnifiedFrame);
- if (!magnifiedFrame.intersects(left, top, right, bottom)) {
- return;
- }
-
- final Rect magnifFrameInScreenCoords = mTempRect1;
- getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords);
-
- final float scrollX;
- final float scrollY;
- if (right - left > magnifFrameInScreenCoords.width()) {
- final int direction = TextUtils
- .getLayoutDirectionFromLocale(Locale.getDefault());
- if (direction == View.LAYOUT_DIRECTION_LTR) {
- scrollX = left - magnifFrameInScreenCoords.left;
- } else {
- scrollX = right - magnifFrameInScreenCoords.right;
- }
- } else if (left < magnifFrameInScreenCoords.left) {
- scrollX = left - magnifFrameInScreenCoords.left;
- } else if (right > magnifFrameInScreenCoords.right) {
- scrollX = right - magnifFrameInScreenCoords.right;
- } else {
- scrollX = 0;
- }
-
- if (bottom - top > magnifFrameInScreenCoords.height()) {
- scrollY = top - magnifFrameInScreenCoords.top;
- } else if (top < magnifFrameInScreenCoords.top) {
- scrollY = top - magnifFrameInScreenCoords.top;
- } else if (bottom > magnifFrameInScreenCoords.bottom) {
- scrollY = bottom - magnifFrameInScreenCoords.bottom;
- } else {
- scrollY = 0;
- }
-
- final float scale = getScale();
- offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID);
- }
- }
-
- private void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
- }
- if (Thread.currentThread().getId() == mMainThreadId) {
- mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
- } else {
- mHandler.obtainMessage(MSG_SEND_SPEC_TO_ANIMATION,
- animate ? 1 : 0, 0, spec).sendToTarget();
- }
+ mDisplay.setForceShowMagnifiableBounds(show);
}
private void onScreenTurnedOff() {
- mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_OFF);
- }
-
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SEND_SPEC_TO_ANIMATION:
- final boolean animate = msg.arg1 == 1;
- final MagnificationSpec spec = (MagnificationSpec) msg.obj;
- mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
- break;
- case MSG_SCREEN_TURNED_OFF:
- resetIfNeeded(false);
- break;
- case MSG_ON_MAGNIFIED_BOUNDS_CHANGED: {
- final SomeArgs args = (SomeArgs) msg.obj;
- final Region magnifiedBounds = (Region) args.arg1;
- onMagnificationRegionChanged(magnifiedBounds);
- magnifiedBounds.recycle();
- args.recycle();
- } break;
- case MSG_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
- final SomeArgs args = (SomeArgs) msg.obj;
- final int left = args.argi1;
- final int top = args.argi2;
- final int right = args.argi3;
- final int bottom = args.argi4;
- requestRectangleOnScreen(left, top, right, bottom);
- args.recycle();
- } break;
- case MSG_ON_USER_CONTEXT_CHANGED:
- resetIfNeeded(true);
- break;
- }
- return true;
+ final Message m = PooledLambda.obtainMessage(
+ mDisplay::resetIfNeeded, false);
+ mHandler.sendMessage(m);
}
@Override
public String toString() {
- return "MagnificationController{" +
- "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
- ", mMagnificationRegion=" + mMagnificationRegion +
- ", mMagnificationBounds=" + mMagnificationBounds +
- ", mUserId=" + mUserId +
- ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
- ", mRegistered=" + mRegistered +
- ", mUnregisterPending=" + mUnregisterPending +
- '}';
+ return mDisplay.toString();
}
/**
@@ -974,6 +1068,7 @@
private static class ScreenStateObserver extends BroadcastReceiver {
private final Context mContext;
private final MagnificationController mController;
+ private boolean mRegistered = false;
public ScreenStateObserver(Context context, MagnificationController controller) {
mContext = context;
@@ -981,11 +1076,17 @@
}
public void register() {
- mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ if (!mRegistered) {
+ mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ mRegistered = true;
+ }
}
public void unregister() {
- mContext.unregisterReceiver(this);
+ if (mRegistered) {
+ mContext.unregisterReceiver(this);
+ mRegistered = false;
+ }
}
@Override
@@ -1002,14 +1103,17 @@
mContentResolver = contentResolver;
}
- public void putMagnificationScale(float value, int userId) {
+ public void putMagnificationScale(float value, int displayId, int userId) {
Settings.Secure.putFloatForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE + (
+ Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
+ value, userId);
}
- public float getMagnificationScale(int userId) {
+ public float getMagnificationScale(int displayId, int userId) {
return Settings.Secure.getFloatForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE
+ + (Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
DEFAULT_MAGNIFICATION_SCALE, userId);
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index da52d40..39866a7 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -56,6 +56,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -629,10 +630,10 @@
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
providerUserId, true);
} else {
- final String dialogMessage = mPackageManagerInternal.getSuspendedDialogMessage(
- providerPackage, providerUserId);
+ final SuspendDialogInfo dialogInfo = mPackageManagerInternal
+ .getSuspendedDialogInfo(providerPackage, providerUserId);
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
- providerPackage, suspendingPackage, dialogMessage, providerUserId);
+ providerPackage, suspendingPackage, dialogInfo, providerUserId);
}
} else if (provider.maskedByQuietProfile) {
showBadge = true;
diff --git a/services/art-profile b/services/art-profile
index 328f8f7..4168a3f 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -207,7 +207,7 @@
HPLandroid/hardware/weaver/V1_0/IWeaver;->setHALInstrumentation()V
HPLandroid/hardware/weaver/V1_0/IWeaver;->unlinkToDeath(Landroid/os/IHwBinder$DeathRecipient;)Z
HPLandroid/hardware/weaver/V1_0/IWeaver;->write(ILjava/util/ArrayList;Ljava/util/ArrayList;)I
-HPLandroid/media/IMediaExtractorUpdateService;->loadPlugins(Ljava/lang/String;)V
+HPLandroid/media/IMediaUpdateService;->loadPlugins(Ljava/lang/String;)V
HPLandroid/net/apf/ApfGenerator$Instruction;-><init>(Landroid/net/apf/ApfGenerator;Landroid/net/apf/ApfGenerator$Opcodes;Landroid/net/apf/ApfGenerator$Register;)V
HPLandroid/net/apf/ApfGenerator$Instruction;->calculateImmSize(IZ)B
HPLandroid/net/apf/ApfGenerator$Instruction;->calculateTargetLabelOffset()I
@@ -3977,9 +3977,9 @@
PLandroid/hardware/weaver/V1_0/WeaverReadResponse;-><init>()V
PLandroid/hardware/weaver/V1_0/WeaverReadResponse;->readEmbeddedFromParcel(Landroid/os/HwParcel;Landroid/os/HwBlob;J)V
PLandroid/hardware/weaver/V1_0/WeaverReadResponse;->readFromParcel(Landroid/os/HwParcel;)V
-PLandroid/media/IMediaExtractorUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-PLandroid/media/IMediaExtractorUpdateService$Stub$Proxy;->loadPlugins(Ljava/lang/String;)V
-PLandroid/media/IMediaExtractorUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaExtractorUpdateService;
+PLandroid/media/IMediaUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+PLandroid/media/IMediaUpdateService$Stub$Proxy;->loadPlugins(Ljava/lang/String;)V
+PLandroid/media/IMediaUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaExtractorUpdateService;
PLandroid/net/apf/-$$Lambda$ApfFilter$UV1wDVoVlbcxpr8zevj_aMFtUGw;-><init>()V
PLandroid/net/apf/-$$Lambda$ApfFilter$UV1wDVoVlbcxpr8zevj_aMFtUGw;->applyAsInt(Ljava/lang/Object;)I
PLandroid/net/apf/ApfCapabilities;-><init>(III)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index af33bd0..d3842b7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -182,9 +182,7 @@
final int userId = users.get(i).id;
final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
if (disabled) {
- if (disabled) {
- Slog.i(TAG, "Disabling Autofill for user " + userId);
- }
+ Slog.i(TAG, "Disabling Autofill for user " + userId);
mDisabledUsers.put(userId, disabled);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 78facf8..14d68cb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -185,23 +185,6 @@
updateLocked(disabled);
}
- @Nullable
- CharSequence getServiceName() {
- final String packageName = getServicePackageName();
- if (packageName == null) {
- return null;
- }
-
- try {
- final PackageManager pm = mContext.getPackageManager();
- final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
- return pm.getApplicationLabel(info);
- } catch (Exception e) {
- Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
- return packageName;
- }
- }
-
@GuardedBy("mLock")
private int getServiceUidLocked() {
if (mInfo == null) {
@@ -226,6 +209,7 @@
return null;
}
+ @Nullable
ComponentName getServiceComponentName() {
synchronized (mLock) {
if (mInfo == null) {
@@ -706,17 +690,27 @@
}
}
- @NonNull
- CharSequence getServiceLabel() {
- final CharSequence label = mInfo.getServiceInfo().loadSafeLabel(
+ /**
+ * Gets the user-visibile name of the service this service binds to, or {@code null} if the
+ * service is disabled.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ public CharSequence getServiceLabelLocked() {
+ return mInfo == null ? null : mInfo.getServiceInfo().loadSafeLabel(
mContext.getPackageManager(), 0 /* do not ellipsize */,
PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
- return label;
}
+ /**
+ * Gets the icon of the service this service binds to, or {@code null} if the service is
+ * disabled.
+ */
@NonNull
- Drawable getServiceIcon() {
- return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
+ @Nullable
+ @GuardedBy("mLock")
+ Drawable getServiceIconLocked() {
+ return mInfo == null ? null : mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
}
/**
@@ -959,7 +953,7 @@
} else {
pw.println();
mInfo.dump(prefix2, pw);
- pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel());
+ pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
}
pw.print(prefix); pw.print("Component from settings: ");
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c1b620c..f85749a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -48,6 +48,7 @@
import android.content.IntentSender;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Build;
@@ -311,8 +312,8 @@
Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
+ mUrlBar.getWebDomain());
}
- final ViewState viewState = new ViewState(Session.this, urlBarId,
- Session.this, ViewState.STATE_URL_BAR);
+ final ViewState viewState = new ViewState(urlBarId, Session.this,
+ ViewState.STATE_URL_BAR);
mViewStates.put(urlBarId, viewState);
}
}
@@ -1749,7 +1750,18 @@
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(mActivityToken, id, client);
- getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
+
+ final CharSequence serviceLabel;
+ final Drawable serviceIcon;
+ synchronized (mLock) {
+ serviceLabel = mService.getServiceLabelLocked();
+ serviceIcon = mService.getServiceIconLocked();
+ }
+ if (serviceLabel == null || serviceIcon == null) {
+ wtf(null, "showSaveLocked(): no service label or icon");
+ return true;
+ }
+ getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
mService.getServicePackageName(), saveInfo, this,
mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode);
if (client != null) {
@@ -1801,8 +1813,6 @@
return sanitizers;
}
- // TODO: this method is called a few times in the save process, we should cache its results into
- // ViewState.
@Nullable
private AutofillValue getSanitizedValue(
@Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
@@ -1810,13 +1820,22 @@
@Nullable AutofillValue value) {
if (sanitizers == null || value == null) return value;
- final InternalSanitizer sanitizer = sanitizers.get(id);
- if (sanitizer == null) {
- return value;
- }
+ final ViewState state = mViewStates.get(id);
+ AutofillValue sanitized = state == null ? null : state.getSanitizedValue();
+ if (sanitized == null) {
+ final InternalSanitizer sanitizer = sanitizers.get(id);
+ if (sanitizer == null) {
+ return value;
+ }
- final AutofillValue sanitized = sanitizer.sanitize(value);
- if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+ sanitized = sanitizer.sanitize(value);
+ if (sDebug) {
+ Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+ }
+ if (state != null) {
+ state.setSanitizedValue(sanitized);
+ }
+ }
return sanitized;
}
@@ -2137,7 +2156,7 @@
|| action == ACTION_VIEW_ENTERED) {
if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
boolean isIgnored = isIgnoredLocked(id);
- viewState = new ViewState(this, id, this,
+ viewState = new ViewState(id, this,
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
mViewStates.put(id, viewState);
@@ -2318,9 +2337,19 @@
filterText = value.getTextValue().toString();
}
+ final CharSequence serviceLabel;
+ final Drawable serviceIcon;
+ synchronized (mLock) {
+ serviceLabel = mService.getServiceLabelLocked();
+ serviceIcon = mService.getServiceIconLocked();
+ }
+ if (serviceLabel == null || serviceIcon == null) {
+ wtf(null, "onFillReady(): no service label or icon");
+ return;
+ }
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- mService.getServiceLabel(), mService.getServiceIcon(), this, id, mCompatMode);
+ serviceLabel, serviceIcon, this, id, mCompatMode);
synchronized (mLock) {
if (mUiShownTime == 0) {
@@ -2607,7 +2636,7 @@
if (viewState != null) {
viewState.setState(state);
} else {
- viewState = new ViewState(this, id, this, state);
+ viewState = new ViewState(id, this, state);
if (sVerbose) {
Slog.v(TAG, "Adding autofillable view with id " + id + " and state " + state);
}
@@ -2655,12 +2684,6 @@
}
}
- CharSequence getServiceName() {
- synchronized (mLock) {
- return mService.getServiceName();
- }
- }
-
// TODO: this should never be null, but we got at least one occurrence, probably due to a race.
@GuardedBy("mLock")
@Nullable
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index a8dae03..2cc6d20 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -77,7 +77,6 @@
public final AutofillId id;
private final Listener mListener;
- private final Session mSession;
private FillResponse mResponse;
private AutofillValue mCurrentValue;
@@ -87,8 +86,7 @@
private int mState;
private String mDatasetId;
- ViewState(Session session, AutofillId id, Listener listener, int state) {
- mSession = session;
+ ViewState(AutofillId id, Listener listener, int state) {
this.id = id;
mListener = listener;
mState = state;
@@ -141,10 +139,6 @@
mResponse = response;
}
- CharSequence getServiceName() {
- return mSession.getServiceName();
- }
-
int getState() {
return mState;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 1b97926..eb31e78 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -159,7 +159,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
-public class BackupManagerService implements BackupManagerServiceInterface {
+public class BackupManagerService {
public static final String TAG = "BackupManagerService";
public static final boolean DEBUG = true;
@@ -701,7 +701,6 @@
// Utility: build a new random integer token. The low bits are the ordinal of the
// operation for near-time uniqueness, and the upper bits are random for app-
// side unpredictability.
- @Override
public int generateRandomIntegerToken() {
int token = mTokenGenerator.nextInt();
if (token < 0) token = -token;
@@ -1108,12 +1107,10 @@
return array;
}
- @Override
public boolean setBackupPassword(String currentPw, String newPw) {
return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
}
- @Override
public boolean hasBackupPassword() {
return mBackupPasswordManager.hasBackupPassword();
}
@@ -1590,7 +1587,6 @@
// Get the restore-set token for the best-available restore set for this package:
// the active set if possible, else the ancestral one. Returns zero if none available.
- @Override
public long getAvailableRestoreToken(String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getAvailableRestoreToken");
@@ -1608,12 +1604,10 @@
return token;
}
- @Override
public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
return requestBackup(packages, observer, null, flags);
}
- @Override
public int requestBackup(String[] packages, IBackupObserver observer,
IBackupManagerMonitor monitor, int flags) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
@@ -1702,7 +1696,6 @@
}
// Cancel all running backups.
- @Override
public void cancelBackups() {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
if (MORE_DEBUG) {
@@ -1732,7 +1725,6 @@
}
}
- @Override
public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
int operationType) {
if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
@@ -1790,7 +1782,6 @@
}
// synchronous waiter case
- @Override
public boolean waitUntilOperationComplete(int token) {
if (MORE_DEBUG) {
Slog.i(TAG, "Blocking until operation complete for "
@@ -1895,7 +1886,6 @@
}
- @Override
public void tearDownAgentAndKill(ApplicationInfo app) {
if (app == null) {
// Null means the system package, so just quietly move on. :)
@@ -2049,7 +2039,6 @@
* @return Whether ongoing work will continue. The return value here will be passed
* along as the return value to the scheduled job's onStartJob() callback.
*/
- @Override
public boolean beginFullBackup(FullBackupJob scheduledJob) {
final long now = System.currentTimeMillis();
final long fullBackupInterval;
@@ -2224,7 +2213,6 @@
// The job scheduler says our constraints don't hold any more,
// so tear down any ongoing backup task right away.
- @Override
public void endFullBackup() {
// offload the mRunningFullBackupTask.handleCancel() call to another thread,
// as we might have to wait for mCancelLock
@@ -2331,7 +2319,6 @@
// ----- IBackupManager binder interface -----
- @Override
public void dataChanged(final String packageName) {
final int callingUserHandle = UserHandle.getCallingUserId();
if (callingUserHandle != UserHandle.USER_SYSTEM) {
@@ -2362,7 +2349,6 @@
}
// Run an initialize operation for the given transport
- @Override
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"initializeTransport");
@@ -2382,7 +2368,6 @@
}
// Clear the given package's backup data from the current transport
- @Override
public void clearBackupData(String transportName, String packageName) {
if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
PackageInfo info;
@@ -2438,7 +2423,6 @@
// Run a backup pass immediately for any applications that have declared
// that they have pending updates.
- @Override
public void backupNow() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
@@ -2480,7 +2464,6 @@
//
// This is the variant used by 'adb backup'; it requires on-screen confirmation
// by the user because it can be used to offload data over untrusted USB.
- @Override
public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
boolean compress, boolean doKeyValue, String[] pkgList) {
@@ -2558,7 +2541,6 @@
}
}
- @Override
public void fullTransportBackup(String[] pkgNames) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"fullTransportBackup");
@@ -2618,7 +2600,6 @@
}
}
- @Override
public void adbRestore(ParcelFileDescriptor fd) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
@@ -2719,7 +2700,6 @@
// Confirm that the previously-requested full backup/restore operation can proceed. This
// is used to require a user-facing disclosure about the operation.
- @Override
public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
if (DEBUG) {
@@ -2819,7 +2799,6 @@
}
// Enable/disable backups
- @Override
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
@@ -2887,7 +2866,6 @@
}
// Enable/disable automatic restore of app data at install time
- @Override
public void setAutoRestore(boolean doAutoRestore) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setAutoRestore");
@@ -2907,7 +2885,6 @@
}
// Mark the backup service as having been provisioned
- @Override
public void setBackupProvisioned(boolean available) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupProvisioned");
@@ -2917,7 +2894,6 @@
}
// Report whether the backup mechanism is currently enabled
- @Override
public boolean isBackupEnabled() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"isBackupEnabled");
@@ -2925,7 +2901,6 @@
}
// Report the name of the currently active transport
- @Override
public String getCurrentTransport() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
@@ -2938,7 +2913,6 @@
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered.
*/
- @Override
@Nullable
public ComponentName getCurrentTransportComponent() {
mContext.enforceCallingOrSelfPermission(
@@ -2954,7 +2928,6 @@
}
// Report all known, available backup transports
- @Override
public String[] listAllTransports() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransports");
@@ -2962,14 +2935,12 @@
return mTransportManager.getRegisteredTransportNames();
}
- @Override
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
return mTransportManager.getRegisteredTransportComponents();
}
- @Override
public String[] getTransportWhitelist() {
// No permission check, intentionally.
Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
@@ -3006,7 +2977,6 @@
* @throws SecurityException If the UID of the calling process differs from the package UID of
* {@code transportComponent} or if the caller does NOT have BACKUP permission.
*/
- @Override
public void updateTransportAttributes(
ComponentName transportComponent,
String name,
@@ -3070,7 +3040,6 @@
}
/** Selects transport {@code transportName} and returns previous selected transport. */
- @Override
@Deprecated
@Nullable
public String selectBackupTransport(String transportName) {
@@ -3089,7 +3058,6 @@
}
}
- @Override
public void selectBackupTransportAsync(
ComponentName transportComponent, ISelectBackupTransportCallback listener) {
mContext.enforceCallingOrSelfPermission(
@@ -3161,7 +3129,6 @@
// Supply the configuration Intent for the given transport. If the name is not one
// of the available transports, or if the transport does not supply any configuration
// UI, the method returns null.
- @Override
public Intent getConfigurationIntent(String transportName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getConfigurationIntent");
@@ -3186,7 +3153,6 @@
* @param transportName The name of the registered transport.
* @return The current destination string or null if the transport is not registered.
*/
- @Override
public String getDestinationString(String transportName) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "getDestinationString");
@@ -3204,7 +3170,6 @@
}
// Supply the manage-data intent for the given transport.
- @Override
public Intent getDataManagementIntent(String transportName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDataManagementIntent");
@@ -3223,7 +3188,6 @@
// Supply the menu label for affordances that fire the manage-data intent
// for the given transport.
- @Override
public String getDataManagementLabel(String transportName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDataManagementLabel");
@@ -3242,7 +3206,6 @@
// Callback: a requested backup agent has been instantiated. This should only
// be called from the Activity Manager.
- @Override
public void agentConnected(String packageName, IBinder agentBinder) {
synchronized (mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
@@ -3261,7 +3224,6 @@
// Callback: a backup agent has failed to come up, or has unexpectedly quit.
// If the agent failed to come up in the first place, the agentBinder argument
// will be null. This should only be called from the Activity Manager.
- @Override
public void agentDisconnected(String packageName) {
// TODO: handle backup being interrupted
synchronized (mAgentConnectLock) {
@@ -3278,7 +3240,6 @@
// An application being installed will need a restore pass, then the Package Manager
// will need to be told when the restore is finished.
- @Override
public void restoreAtInstall(String packageName, int token) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
@@ -3364,7 +3325,6 @@
}
// Hand off a restore session
- @Override
public IRestoreSession beginRestoreSession(String packageName, String transport) {
if (DEBUG) {
Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
@@ -3430,7 +3390,6 @@
// Note that a currently-active backup agent has notified us that it has
// completed the given outstanding asynchronous backup/restore operation.
- @Override
public void opComplete(int token, long result) {
if (MORE_DEBUG) {
Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
@@ -3468,7 +3427,6 @@
}
}
- @Override
public boolean isAppEligibleForBackup(String packageName) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
@@ -3490,7 +3448,6 @@
}
}
- @Override
public String[] filterAppsEligibleForBackup(String[] packages) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
@@ -3517,7 +3474,6 @@
}
}
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
@@ -3667,7 +3623,6 @@
}
- @Override
public IBackupManager getBackupManagerBinder() {
return mBackupManagerBinder;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
deleted file mode 100644
index a38a0e9..0000000
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.backup;
-
-import android.annotation.Nullable;
-import android.app.IBackupAgent;
-import android.app.backup.IBackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.IRestoreSession;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Interface for BackupManagerService.
- *
- * Current and future implementations of BackupManagerService should use this interface, so that
- * Trampoline is able to switch between them.
- */
-public interface BackupManagerServiceInterface {
-
- void unlockSystemUser();
-
- // Utility: build a new random integer token
- int generateRandomIntegerToken();
-
- boolean setBackupPassword(String currentPw, String newPw);
-
- boolean hasBackupPassword();
-
- // Get the restore-set token for the best-available restore set for this package:
- // the active set if possible, else the ancestral one. Returns zero if none available.
- long getAvailableRestoreToken(String packageName);
-
- int requestBackup(String[] packages, IBackupObserver observer, int flags);
-
- int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags);
-
- // Cancel all running backups.
- void cancelBackups();
-
- void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
- int operationType);
-
- // synchronous waiter case
- boolean waitUntilOperationComplete(int token);
-
- void tearDownAgentAndKill(ApplicationInfo app);
-
- boolean beginFullBackup(FullBackupJob scheduledJob);
-
- // The job scheduler says our constraints don't hold any more,
- // so tear down any ongoing backup task right away.
- void endFullBackup();
-
- void dataChanged(String packageName);
-
- // Initialize the given transport
- void initializeTransports(String[] transportName, IBackupObserver observer);
-
- // Clear the given package's backup data from the current transport
- void clearBackupData(String transportName, String packageName);
-
- // Run a backup pass immediately for any applications that have declared
- // that they have pending updates.
- void backupNow();
-
- // Run a backup pass for the given packages, writing the resulting data stream
- // to the supplied file descriptor. This method is synchronous and does not return
- // to the caller until the backup has been completed.
- //
- // This is the variant used by 'adb backup'; it requires on-screen confirmation
- // by the user because it can be used to offload data over untrusted USB.
- void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
- boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
- boolean compress, boolean doKeyValue, String[] pkgList);
-
- void fullTransportBackup(String[] pkgNames);
-
- void adbRestore(ParcelFileDescriptor fd);
-
- // Confirm that the previously-requested full backup/restore operation can proceed. This
- // is used to require a user-facing disclosure about the operation.
- void acknowledgeAdbBackupOrRestore(int token, boolean allow,
- String curPassword, String encPpassword, IFullBackupRestoreObserver observer);
-
- // Enable/disable backups
- void setBackupEnabled(boolean enable);
-
- // Enable/disable automatic restore of app data at install time
- void setAutoRestore(boolean doAutoRestore);
-
- // Mark the backup service as having been provisioned
- void setBackupProvisioned(boolean available);
-
- // Report whether the backup mechanism is currently enabled
- boolean isBackupEnabled();
-
- // Update the transport attributes
- void updateTransportAttributes(
- ComponentName transportComponent,
- String name,
- Intent configurationIntent,
- String currentDestinationString,
- Intent dataManagementIntent,
- String dataManagementLabel);
-
- // Report the name of the currently active transport
- String getCurrentTransport();
-
- // Report the component name of the host service of the currently active transport
- @Nullable
- ComponentName getCurrentTransportComponent();
-
- // Report all known, available backup transports
- String[] listAllTransports();
-
- ComponentName[] listAllTransportComponents();
-
- String[] getTransportWhitelist();
-
- // Select which transport to use for the next backup operation.
- String selectBackupTransport(String transport);
-
- void selectBackupTransportAsync(ComponentName transport,
- ISelectBackupTransportCallback listener);
-
- // Supply the configuration Intent for the given transport. If the name is not one
- // of the available transports, or if the transport does not supply any configuration
- // UI, the method returns null.
- Intent getConfigurationIntent(String transportName);
-
- // Supply the configuration summary string for the given transport. If the name is
- // not one of the available transports, or if the transport does not supply any
- // summary / destination string, the method can return null.
- //
- // This string is used VERBATIM as the summary text of the relevant Settings item!
- String getDestinationString(String transportName);
-
- // Supply the manage-data intent for the given transport.
- Intent getDataManagementIntent(String transportName);
-
- // Supply the menu label for affordances that fire the manage-data intent
- // for the given transport.
- String getDataManagementLabel(String transportName);
-
- // Callback: a requested backup agent has been instantiated. This should only
- // be called from the Activity Manager.
- void agentConnected(String packageName, IBinder agentBinder);
-
- // Callback: a backup agent has failed to come up, or has unexpectedly quit.
- // If the agent failed to come up in the first place, the agentBinder argument
- // will be null. This should only be called from the Activity Manager.
- void agentDisconnected(String packageName);
-
- // An application being installed will need a restore pass, then the Package Manager
- // will need to be told when the restore is finished.
- void restoreAtInstall(String packageName, int token);
-
- // Hand off a restore session
- IRestoreSession beginRestoreSession(String packageName, String transport);
-
- // Note that a currently-active backup agent has notified us that it has
- // completed the given outstanding asynchronous backup/restore operation.
- void opComplete(int token, long result);
-
- boolean isAppEligibleForBackup(String packageName);
-
- String[] filterAppsEligibleForBackup(String[] packages);
-
- void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
- IBackupManager getBackupManagerBinder();
-
- // Gets access to the backup/restore agent timeout parameters.
- BackupAgentTimeoutParameters getAgentTimeoutParameters();
-}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index fbec5cb..bb14576 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -39,7 +39,7 @@
private static final String TAG = "KeyValueAdbRestoreEngine";
private static final boolean DEBUG = false;
- private final BackupManagerServiceInterface mBackupManagerService;
+ private final BackupManagerService mBackupManagerService;
private final File mDataDir;
FileMetadata mInfo;
@@ -48,7 +48,7 @@
IBackupAgent mAgent;
int mToken;
- public KeyValueAdbRestoreEngine(BackupManagerServiceInterface backupManagerService,
+ public KeyValueAdbRestoreEngine(BackupManagerService backupManagerService,
File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
int token) {
mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 787d667..818154b 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -76,7 +76,7 @@
final Context mContext;
final File mSuppressFile; // existence testing & creating synchronized on 'this'
final boolean mGlobalDisable;
- volatile BackupManagerServiceInterface mService;
+ volatile BackupManagerService mService;
private HandlerThread mHandlerThread;
@@ -100,7 +100,7 @@
BACKUP_SUPPRESS_FILENAME);
}
- protected BackupManagerServiceInterface createBackupManagerService() {
+ protected BackupManagerService createBackupManagerService() {
return BackupManagerService.create(mContext, this, mHandlerThread);
}
@@ -135,7 +135,7 @@
initialize(UserHandle.USER_SYSTEM);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
Slog.i(TAG, "Unlocking system user; mService=" + mService);
if (svc != null) {
svc.unlockSystemUser();
@@ -198,7 +198,7 @@
@Override
public void dataChanged(String packageName) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.dataChanged(packageName);
}
@@ -207,7 +207,7 @@
@Override
public void initializeTransports(String[] transportNames, IBackupObserver observer)
throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.initializeTransports(transportNames, observer);
}
@@ -216,7 +216,7 @@
@Override
public void clearBackupData(String transportName, String packageName)
throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.clearBackupData(transportName, packageName);
}
@@ -224,7 +224,7 @@
@Override
public void agentConnected(String packageName, IBinder agent) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.agentConnected(packageName, agent);
}
@@ -232,7 +232,7 @@
@Override
public void agentDisconnected(String packageName) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.agentDisconnected(packageName);
}
@@ -240,7 +240,7 @@
@Override
public void restoreAtInstall(String packageName, int token) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.restoreAtInstall(packageName, token);
}
@@ -248,7 +248,7 @@
@Override
public void setBackupEnabled(boolean isEnabled) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.setBackupEnabled(isEnabled);
}
@@ -256,7 +256,7 @@
@Override
public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.setAutoRestore(doAutoRestore);
}
@@ -264,7 +264,7 @@
@Override
public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.setBackupProvisioned(isProvisioned);
}
@@ -272,25 +272,25 @@
@Override
public boolean isBackupEnabled() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.isBackupEnabled() : false;
}
@Override
public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.setBackupPassword(currentPw, newPw) : false;
}
@Override
public boolean hasBackupPassword() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.hasBackupPassword() : false;
}
@Override
public void backupNow() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.backupNow();
}
@@ -301,7 +301,7 @@
boolean includeShared, boolean doWidgets, boolean allApps,
boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames)
throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets,
allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
@@ -310,7 +310,7 @@
@Override
public void fullTransportBackup(String[] packageNames) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.fullTransportBackup(packageNames);
}
@@ -318,7 +318,7 @@
@Override
public void adbRestore(ParcelFileDescriptor fd) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.adbRestore(fd);
}
@@ -328,7 +328,7 @@
public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
String encryptionPassword, IFullBackupRestoreObserver observer)
throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.acknowledgeAdbBackupOrRestore(token, allow,
curPassword, encryptionPassword, observer);
@@ -337,7 +337,7 @@
@Override
public String getCurrentTransport() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getCurrentTransport() : null;
}
@@ -348,25 +348,25 @@
@Override
@Nullable
public ComponentName getCurrentTransportComponent() {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getCurrentTransportComponent() : null;
}
@Override
public String[] listAllTransports() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.listAllTransports() : null;
}
@Override
public ComponentName[] listAllTransportComponents() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.listAllTransportComponents() : null;
}
@Override
public String[] getTransportWhitelist() {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getTransportWhitelist() : null;
}
@@ -378,7 +378,7 @@
String currentDestinationString,
@Nullable Intent dataManagementIntent,
String dataManagementLabel) {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.updateTransportAttributes(
transportComponent,
@@ -392,14 +392,14 @@
@Override
public String selectBackupTransport(String transport) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.selectBackupTransport(transport) : null;
}
@Override
public void selectBackupTransportAsync(ComponentName transport,
ISelectBackupTransportCallback listener) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.selectBackupTransportAsync(transport, listener);
} else {
@@ -415,38 +415,38 @@
@Override
public Intent getConfigurationIntent(String transport) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getConfigurationIntent(transport) : null;
}
@Override
public String getDestinationString(String transport) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getDestinationString(transport) : null;
}
@Override
public Intent getDataManagementIntent(String transport) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getDataManagementIntent(transport) : null;
}
@Override
public String getDataManagementLabel(String transport) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getDataManagementLabel(transport) : null;
}
@Override
public IRestoreSession beginRestoreSession(String packageName, String transportID)
throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
}
@Override
public void opComplete(int token, long result) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.opComplete(token, result);
}
@@ -454,26 +454,26 @@
@Override
public long getAvailableRestoreToken(String packageName) {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
}
@Override
public boolean isAppEligibleForBackup(String packageName) {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
}
@Override
public String[] filterAppsEligibleForBackup(String[] packages) {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
}
@Override
public int requestBackup(String[] packages, IBackupObserver observer,
IBackupManagerMonitor monitor, int flags) throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc == null) {
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
@@ -482,7 +482,7 @@
@Override
public void cancelBackups() throws RemoteException {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.cancelBackups();
}
@@ -492,7 +492,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.dump(fd, pw, args);
} else {
@@ -503,12 +503,12 @@
// Full backup/restore entry points - non-Binder; called directly
// by the full-backup scheduled job
/* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
}
/* package */ void endFullBackup() {
- BackupManagerServiceInterface svc = mService;
+ BackupManagerService svc = mService;
if (svc != null) {
svc.endFullBackup();
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4b77c69..1c8d99a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -189,6 +191,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -257,6 +260,14 @@
@GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
+ /**
+ * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
+ * handler thread, they don't need a lock.
+ */
+ private SparseIntArray mUidRules = new SparseIntArray();
+ /** Flag indicating if background data is restricted. */
+ private boolean mRestrictBackground;
+
final private Context mContext;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -419,6 +430,16 @@
// Handle private DNS validation status updates.
private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
+ /**
+ * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
+ */
+ private static final int EVENT_UID_RULES_CHANGED = 39;
+
+ /**
+ * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
+ */
+ private static final int EVENT_DATA_SAVER_CHANGED = 40;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -780,6 +801,9 @@
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ // To ensure uid rules are synchronized with Network Policy, register for
+ // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
+ // reading existing policy from disk.
try {
mPolicyManager.registerListener(mPolicyListener);
} catch (RemoteException e) {
@@ -910,7 +934,8 @@
registerPrivateDnsSettingsCallbacks();
}
- private Tethering makeTethering() {
+ @VisibleForTesting
+ protected Tethering makeTethering() {
// TODO: Move other elements into @Overridden getters.
final TetheringDependencies deps = new TetheringDependencies() {
@Override
@@ -1116,11 +1141,6 @@
if (ignoreBlocked) {
return false;
}
- // Networks are never blocked for system services
- // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked.
- if (isSystem(uid)) {
- return false;
- }
synchronized (mVpns) {
final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
if (vpn != null && vpn.isBlockingUid(uid)) {
@@ -1150,6 +1170,17 @@
mNetworkInfoBlockingLogs.log(action + " " + uid);
}
+ private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
+ boolean blocked) {
+ if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
+ return;
+ }
+ String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
+ nri.mUid, nri.request.requestId, net.netId));
+ mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+ }
+
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1651,10 +1682,17 @@
private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
- // TODO: notify UID when it has requested targeted updates
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
}
@Override
public void onRestrictBackgroundChanged(boolean restrictBackground) {
+ // caller is NPMS, since we only register with them
+ if (LOGD_BLOCKED_NETWORKINFO) {
+ log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+
// TODO: relocate this specific callback in Tethering.
if (restrictBackground) {
log("onRestrictBackgroundChanged(true): disabling tethering");
@@ -1663,6 +1701,50 @@
}
};
+ void handleUidRulesChanged(int uid, int newRules) {
+ // skip update when we've already applied rules
+ final int oldRules = mUidRules.get(uid, RULE_NONE);
+ if (oldRules == newRules) return;
+
+ maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
+
+ if (newRules == RULE_NONE) {
+ mUidRules.delete(uid);
+ } else {
+ mUidRules.put(uid, newRules);
+ }
+ }
+
+ void handleRestrictBackgroundChanged(boolean restrictBackground) {
+ if (mRestrictBackground == restrictBackground) return;
+
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ final boolean curMetered = nai.networkCapabilities.isMetered();
+ maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+ restrictBackground);
+ }
+
+ mRestrictBackground = restrictBackground;
+ }
+
+ private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted) {
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+ // Because the return value of this function depends on the list of UIDs the
+ // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
+ // list all state depending on the return value of this function has to be recomputed.
+ // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
+ // send the necessary onBlockedStatusChanged callbacks.
+ if (vpn != null && vpn.isBlockingUid(uid)) {
+ return true;
+ }
+ }
+
+ return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+ isNetworkMetered, isBackgroundRestricted);
+ }
+
/**
* Require that the caller is either in the same user or has appropriate permission to interact
* across users.
@@ -1679,6 +1761,16 @@
"ConnectivityService");
}
+ private void enforceAnyPermissionOf(String... permissions) {
+ for (String permission : permissions) {
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ throw new SecurityException(
+ "Requires one of the following permissions: " + String.join(", ", permissions) + ".");
+ }
+
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -1723,6 +1815,13 @@
"ConnectivityService");
}
+ private void enforceNetworkStackSettingsOrSetup() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK);
+ }
+
private boolean checkNetworkStackPermission() {
return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
android.Manifest.permission.NETWORK_STACK);
@@ -2101,6 +2200,28 @@
pw.decreaseIndent();
pw.println();
+ pw.print("Restrict background: ");
+ pw.println(mRestrictBackground);
+ pw.println();
+
+ pw.println("Status for known UIDs:");
+ pw.increaseIndent();
+ final int size = mUidRules.size();
+ for (int i = 0; i < size; i++) {
+ // Don't crash if the array is modified while dumping in bugreports.
+ try {
+ final int uid = mUidRules.keyAt(i);
+ final int uidRules = mUidRules.get(uid, RULE_NONE);
+ pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ pw.println(" ArrayIndexOutOfBoundsException");
+ } catch (ConcurrentModificationException e) {
+ pw.println(" ConcurrentModificationException");
+ }
+ }
+ pw.println();
+ pw.decreaseIndent();
+
pw.println("Network Requests:");
pw.increaseIndent();
dumpNetworkRequests(pw);
@@ -3178,6 +3299,12 @@
handlePrivateDnsValidationUpdate(
(PrivateDnsValidationUpdate) msg.obj);
break;
+ case EVENT_UID_RULES_CHANGED:
+ handleUidRulesChanged(msg.arg1, msg.arg2);
+ break;
+ case EVENT_DATA_SAVER_CHANGED:
+ handleRestrictBackgroundChanged(toBool(msg.arg1));
+ break;
}
}
}
@@ -3478,7 +3605,7 @@
ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
- mProxyTracker.sendProxyBroadcast(mProxyTracker.getDefaultProxy());
+ mProxyTracker.sendProxyBroadcast();
}
}
@@ -3766,6 +3893,8 @@
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
+ // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+ // necessary onBlockedStatusChanged callbacks.
mLockdownTracker = null;
if (existing != null) {
existing.shutdown();
@@ -3984,7 +4113,7 @@
@Override
public void setAirplaneMode(boolean enable) {
- enforceConnectivityInternalPermission();
+ enforceNetworkStackSettingsOrSetup();
final long ident = Binder.clearCallingIdentity();
try {
final ContentResolver cr = mContext.getContentResolver();
@@ -4765,15 +4894,14 @@
}
}
- private String getNetworkPermission(NetworkCapabilities nc) {
- // TODO: make these permission strings AIDL constants instead.
+ private int getNetworkPermission(NetworkCapabilities nc) {
if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
- return NetworkManagementService.PERMISSION_SYSTEM;
+ return INetd.PERMISSION_SYSTEM;
}
if (!nc.hasCapability(NET_CAPABILITY_FOREGROUND)) {
- return NetworkManagementService.PERMISSION_NETWORK;
+ return INetd.PERMISSION_NETWORK;
}
- return null;
+ return INetd.PERMISSION_NONE;
}
/**
@@ -4846,9 +4974,9 @@
if (Objects.equals(nai.networkCapabilities, newNc)) return;
- final String oldPermission = getNetworkPermission(nai.networkCapabilities);
- final String newPermission = getNetworkPermission(newNc);
- if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
+ final int oldPermission = getNetworkPermission(nai.networkCapabilities);
+ final int newPermission = getNetworkPermission(newNc);
+ if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
try {
mNMS.setNetworkPermission(nai.network.netId, newPermission);
} catch (RemoteException e) {
@@ -4877,12 +5005,20 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
- // Report changes that are interesting for network statistics tracking.
if (prevNc != null) {
- final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
- newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ final boolean oldMetered = prevNc.isMetered();
+ final boolean newMetered = newNc.isMetered();
+ final boolean meteredChanged = oldMetered != newMetered;
+
+ if (meteredChanged) {
+ maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
+ mRestrictBackground);
+ }
+
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ // Report changes that are interesting for network statistics tracking.
if (meteredChanged || roamingChanged) {
notifyIfacesChangedForNetworkStats();
}
@@ -5012,6 +5148,8 @@
case ConnectivityManager.CALLBACK_AVAILABLE: {
putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ // For this notification, arg1 contains the blocked status.
+ msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_LOSING: {
@@ -5029,6 +5167,10 @@
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
break;
}
+ case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+ msg.arg1 = arg1;
+ break;
+ }
}
msg.what = notificationType;
msg.setData(bundle);
@@ -5584,7 +5726,76 @@
return;
}
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+ metered, mRestrictBackground);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+ }
+
+ /**
+ * Notify of the blocked state apps with a registered callback matching a given NAI.
+ *
+ * Unlike other callbacks, blocked status is different between each individual uid. So for
+ * any given nai, all requests need to be considered according to the uid who filed it.
+ *
+ * @param nai The target NetworkAgentInfo.
+ * @param oldMetered True if the previous network capabilities is metered.
+ * @param newRestrictBackground True if data saver is enabled.
+ */
+ private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
+ boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ final int uidRules = mUidRules.get(nri.mUid);
+ final boolean oldBlocked, newBlocked;
+ // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
+ // between these two calls.
+ synchronized (mVpns) {
+ oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
+ oldRestrictBackground);
+ newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
+ newRestrictBackground);
+ }
+ if (oldBlocked != newBlocked) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+ encodeBool(newBlocked));
+ }
+ }
+ }
+
+ /**
+ * Notify apps with a given UID of the new blocked state according to new uid rules.
+ * @param uid The uid for which the rules changed.
+ * @param newRules The new rules to apply.
+ */
+ private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean oldBlocked, newBlocked;
+ // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
+ // rules changed event. And this function actually loop through all connected nai and
+ // its requests. It seems that mVpns lock will be grabbed frequently in this case.
+ // Reduce the number of locking or optimize the use of lock are likely needed in future.
+ synchronized (mVpns) {
+ oldBlocked = isUidNetworkingWithVpnBlocked(
+ uid, mUidRules.get(uid), metered, mRestrictBackground);
+ newBlocked = isUidNetworkingWithVpnBlocked(
+ uid, newRules, metered, mRestrictBackground);
+ }
+ if (oldBlocked == newBlocked) {
+ return;
+ }
+ final int arg = encodeBool(newBlocked);
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ if (nri != null && nri.mUid == uid) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+ }
+ }
+ }
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 26421a2..0b30ff5c 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -242,11 +242,9 @@
private ActivityTaskManagerInternal mLocalActivityTaskManager;
private PowerManagerInternal mLocalPowerManager;
private PowerManager mPowerManager;
- private ConnectivityService mConnectivityService;
private INetworkPolicyManager mNetworkPolicyManager;
private SensorManager mSensorManager;
private Sensor mMotionSensor;
- private LocationManager mLocationManager;
private LocationRequest mLocationRequest;
private Intent mIdleIntent;
private Intent mLightIdleIntent;
@@ -1508,6 +1506,8 @@
static class Injector {
private final Context mContext;
+ private ConnectivityService mConnectivityService;
+ private LocationManager mLocationManager;
Injector(Context ctx) {
mContext = ctx;
@@ -1527,7 +1527,11 @@
}
ConnectivityService getConnectivityService() {
- return (ConnectivityService) ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ if (mConnectivityService == null) {
+ mConnectivityService = (ConnectivityService) ServiceManager.getService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ return mConnectivityService;
}
Constants getConstants(DeviceIdleController controller, Handler handler,
@@ -1536,7 +1540,10 @@
}
LocationManager getLocationManager() {
- return mContext.getSystemService(LocationManager.class);
+ if (mLocationManager == null) {
+ mLocationManager = mContext.getSystemService(LocationManager.class);
+ }
+ return mLocationManager;
}
MyHandler getHandler(DeviceIdleController controller) {
@@ -1666,7 +1673,6 @@
mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"deviceidle_going_idle");
mGoingIdleWakeLock.setReferenceCounted(true);
- mConnectivityService = mInjector.getConnectivityService();
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
@@ -1689,7 +1695,6 @@
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
- mLocationManager = mInjector.getLocationManager();
mLocationRequest = new LocationRequest()
.setQuality(LocationRequest.ACCURACY_FINE)
.setInterval(0)
@@ -2160,10 +2165,17 @@
}
}
+ @VisibleForTesting
+ boolean isNetworkConnected() {
+ synchronized (this) {
+ return mNetworkConnected;
+ }
+ }
+
void updateConnectivityState(Intent connIntent) {
ConnectivityService cm;
synchronized (this) {
- cm = mConnectivityService;
+ cm = mInjector.getConnectivityService();
}
if (cm == null) {
return;
@@ -2276,13 +2288,17 @@
/** Must only be used in tests. */
@VisibleForTesting
void setDeepEnabledForTest(boolean enabled) {
- mDeepEnabled = enabled;
+ synchronized (this) {
+ mDeepEnabled = enabled;
+ }
}
/** Must only be used in tests. */
@VisibleForTesting
void setLightEnabledForTest(boolean enabled) {
- mLightEnabled = enabled;
+ synchronized (this) {
+ mLightEnabled = enabled;
+ }
}
void becomeInactiveIfAppropriateLocked() {
@@ -2338,7 +2354,9 @@
*/
@VisibleForTesting
void setLightStateForTest(int lightState) {
- mLightState = lightState;
+ synchronized (this) {
+ mLightState = lightState;
+ }
}
@VisibleForTesting
@@ -2429,12 +2447,6 @@
}
}
- /** Must only be used in tests. */
- @VisibleForTesting
- void setLocationManagerForTest(LocationManager lm) {
- mLocationManager = lm;
- }
-
@VisibleForTesting
int getState() {
return mState;
@@ -2486,18 +2498,19 @@
if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
EventLogTags.writeDeviceIdle(mState, reason);
scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
- if (mLocationManager != null
- && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
- mLocationManager.requestLocationUpdates(mLocationRequest,
+ LocationManager locationManager = mInjector.getLocationManager();
+ if (locationManager != null
+ && locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
+ locationManager.requestLocationUpdates(mLocationRequest,
mGenericLocationListener, mHandler.getLooper());
mLocating = true;
} else {
mHasNetworkLocation = false;
}
- if (mLocationManager != null
- && mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
+ if (locationManager != null
+ && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
mHasGps = true;
- mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
mGpsLocationListener, mHandler.getLooper());
mLocating = true;
} else {
@@ -2575,7 +2588,9 @@
/** Must only be used in tests. */
@VisibleForTesting
void setActiveIdleOpsForTest(int count) {
- mActiveIdleOpCount = count;
+ synchronized (this) {
+ mActiveIdleOpCount = count;
+ }
}
void setJobsActive(boolean active) {
@@ -2751,8 +2766,9 @@
void cancelLocatingLocked() {
if (mLocating) {
- mLocationManager.removeUpdates(mGenericLocationListener);
- mLocationManager.removeUpdates(mGpsLocationListener);
+ LocationManager locationManager = mInjector.getLocationManager();
+ locationManager.removeUpdates(mGenericLocationListener);
+ locationManager.removeUpdates(mGpsLocationListener);
mLocating = false;
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 232c151..93bdcbb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -76,6 +76,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -99,6 +100,7 @@
import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.MockProvider;
import com.android.server.location.PassiveProvider;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -1764,9 +1766,7 @@
if (enabled) {
p.enable();
- if (listeners > 0) {
- applyRequirementsLocked(provider);
- }
+ applyRequirementsLocked(provider);
} else {
p.disable();
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index cf39e95..f510d83 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -170,19 +170,6 @@
*/
public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
- /**
- * String to pass to netd to indicate that a network is only accessible
- * to apps that have the CHANGE_NETWORK_STATE permission.
- */
- public static final String PERMISSION_NETWORK = "NETWORK";
-
- /**
- * String to pass to netd to indicate that a network is only
- * accessible to system apps and those with the CONNECTIVITY_INTERNAL
- * permission.
- */
- public static final String PERMISSION_SYSTEM = "SYSTEM";
-
static class NetdResponseCode {
/* Keep in sync with system/netd/server/ResponseCode.h */
public static final int InterfaceListResult = 110;
@@ -223,6 +210,9 @@
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
+ static final boolean MODIFY_OPERATION_ADD = true;
+ static final boolean MODIFY_OPERATION_REMOVE = false;
+
/**
* Binder context for this service
*/
@@ -1122,41 +1112,47 @@
@Override
public void addRoute(int netId, RouteInfo route) {
- modifyRoute("add", "" + netId, route);
+ modifyRoute(MODIFY_OPERATION_ADD, netId, route);
}
@Override
public void removeRoute(int netId, RouteInfo route) {
- modifyRoute("remove", "" + netId, route);
+ modifyRoute(MODIFY_OPERATION_REMOVE, netId, route);
}
- private void modifyRoute(String action, String netId, RouteInfo route) {
+ private void modifyRoute(boolean add, int netId, RouteInfo route) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final Command cmd = new Command("network", "route", action, netId);
-
- // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
- cmd.appendArg(route.getInterface());
- cmd.appendArg(route.getDestination().toString());
+ final String ifName = route.getInterface();
+ final String dst = route.getDestination().toString();
+ final String nextHop;
switch (route.getType()) {
case RouteInfo.RTN_UNICAST:
if (route.hasGateway()) {
- cmd.appendArg(route.getGateway().getHostAddress());
+ nextHop = route.getGateway().getHostAddress();
+ } else {
+ nextHop = INetd.NEXTHOP_NONE;
}
break;
case RouteInfo.RTN_UNREACHABLE:
- cmd.appendArg("unreachable");
+ nextHop = INetd.NEXTHOP_UNREACHABLE;
break;
case RouteInfo.RTN_THROW:
- cmd.appendArg("throw");
+ nextHop = INetd.NEXTHOP_THROW;
+ break;
+ default:
+ nextHop = INetd.NEXTHOP_NONE;
break;
}
-
try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ if (add) {
+ mNetdService.networkAddRoute(netId, ifName, dst, nextHop);
+ } else {
+ mNetdService.networkRemoveRoute(netId, ifName, dst, nextHop);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1916,44 +1912,21 @@
@Override
public void addVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
- argv[0] = "users";
- argv[1] = "add";
- argv[2] = netId;
- int argc = 3;
- // Avoid overly long commands by limiting number of UID ranges per command.
- for (int i = 0; i < ranges.length; i++) {
- argv[argc++] = ranges[i].toString();
- if (i == (ranges.length - 1) || argc == argv.length) {
- try {
- mConnector.execute("network", Arrays.copyOf(argv, argc));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- argc = 3;
- }
+
+ try {
+ mNetdService.networkAddUidRanges(netId, ranges);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@Override
public void removeVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
- argv[0] = "users";
- argv[1] = "remove";
- argv[2] = netId;
- int argc = 3;
- // Avoid overly long commands by limiting number of UID ranges per command.
- for (int i = 0; i < ranges.length; i++) {
- argv[argc++] = ranges[i].toString();
- if (i == (ranges.length - 1) || argc == argv.length) {
- try {
- mConnector.execute("network", Arrays.copyOf(argv, argc));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- argc = 3;
- }
+ try {
+ mNetdService.networkRemoveUidRanges(netId, ranges);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2412,17 +2385,13 @@
}
@Override
- public void createPhysicalNetwork(int netId, String permission) {
+ public void createPhysicalNetwork(int netId, int permission) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- if (permission != null) {
- mConnector.execute("network", "create", netId, permission);
- } else {
- mConnector.execute("network", "create", netId);
- }
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkCreatePhysical(netId, permission);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2431,10 +2400,9 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "create", netId, "vpn", hasDNS ? "1" : "0",
- secure ? "1" : "0");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkCreateVpn(netId, hasDNS, secure);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2455,20 +2423,24 @@
@Override
public void addInterfaceToNetwork(String iface, int netId) {
- modifyInterfaceInNetwork("add", "" + netId, iface);
+ modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, netId, iface);
}
@Override
public void removeInterfaceFromNetwork(String iface, int netId) {
- modifyInterfaceInNetwork("remove", "" + netId, iface);
+ modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, netId, iface);
}
- private void modifyInterfaceInNetwork(String action, String netId, String iface) {
+ private void modifyInterfaceInNetwork(boolean add, int netId, String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "interface", action, netId, iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ if (add) {
+ mNetdService.networkAddInterface(netId, iface);
+ } else {
+ mNetdService.networkRemoveInterface(netId, iface);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2476,20 +2448,20 @@
public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final Command cmd = new Command("network", "route", "legacy", uid, "add", netId);
-
- // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
final LinkAddress la = routeInfo.getDestinationLinkAddress();
- cmd.appendArg(routeInfo.getInterface());
- cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getPrefixLength());
- if (routeInfo.hasGateway()) {
- cmd.appendArg(routeInfo.getGateway().getHostAddress());
- }
+ final String ifName = routeInfo.getInterface();
+ final String dst = la.toString();
+ final String nextHop;
+ if (routeInfo.hasGateway()) {
+ nextHop = routeInfo.getGateway().getHostAddress();
+ } else {
+ nextHop = "";
+ }
try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkAddLegacyRoute(netId, ifName, dst, nextHop, uid);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2498,9 +2470,9 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "default", "set", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkSetDefault(netId);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2509,49 +2481,41 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "default", "clear");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkClearDefault();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@Override
- public void setNetworkPermission(int netId, String permission) {
+ public void setNetworkPermission(int netId, int permission) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- if (permission != null) {
- mConnector.execute("network", "permission", "network", "set", permission, netId);
- } else {
- mConnector.execute("network", "permission", "network", "clear", netId);
- }
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkSetPermissionForNetwork(netId, permission);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
+ private int parsePermission(String permission) {
+ if (permission.equals("NETWORK")) {
+ return INetd.PERMISSION_NETWORK;
+ }
+ if (permission.equals("SYSTEM")) {
+ return INetd.PERMISSION_SYSTEM;
+ }
+ return INetd.PERMISSION_NONE;
+ }
@Override
public void setPermission(String permission, int[] uids) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- Object[] argv = new Object[4 + MAX_UID_RANGES_PER_COMMAND];
- argv[0] = "permission";
- argv[1] = "user";
- argv[2] = "set";
- argv[3] = permission;
- int argc = 4;
- // Avoid overly long commands by limiting number of UIDs per command.
- for (int i = 0; i < uids.length; ++i) {
- argv[argc++] = uids[i];
- if (i == uids.length - 1 || argc == argv.length) {
- try {
- mConnector.execute("network", Arrays.copyOf(argv, argc));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- argc = 4;
- }
+ try {
+ mNetdService.networkSetPermissionForUser(parsePermission(permission), uids);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2559,22 +2523,10 @@
public void clearPermission(int[] uids) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
- argv[0] = "permission";
- argv[1] = "user";
- argv[2] = "clear";
- int argc = 3;
- // Avoid overly long commands by limiting number of UIDs per command.
- for (int i = 0; i < uids.length; ++i) {
- argv[argc++] = uids[i];
- if (i == uids.length - 1 || argc == argv.length) {
- try {
- mConnector.execute("network", Arrays.copyOf(argv, argc));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- argc = 3;
- }
+ try {
+ mNetdService.networkClearPermissionForUser(uids);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2583,9 +2535,9 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "protect", "allow", uid);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkSetProtectAllow(uid);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -2594,26 +2546,26 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("network", "protect", "deny", uid);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.networkSetProtectDeny(uid);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@Override
public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
- modifyInterfaceInNetwork("add", "local", iface);
+ modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.NETID_LOCAL, iface);
for (RouteInfo route : routes) {
if (!route.isDefaultRoute()) {
- modifyRoute("add", "local", route);
+ modifyRoute(MODIFY_OPERATION_ADD, INetd.NETID_LOCAL, route);
}
}
}
@Override
public void removeInterfaceFromLocalNetwork(String iface) {
- modifyInterfaceInNetwork("remove", "local", iface);
+ modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, INetd.NETID_LOCAL, iface);
}
@Override
@@ -2622,7 +2574,7 @@
for (RouteInfo route : routes) {
try {
- modifyRoute("remove", "local", route);
+ modifyRoute(MODIFY_OPERATION_REMOVE, INetd.NETID_LOCAL, route);
} catch (IllegalStateException e) {
failures++;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7c67596..858dced 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3150,10 +3150,10 @@
if (toSystem) {
// Everything else goes into sandbox.
- return device + "Android/sandbox/" + sandboxId.replace(':', '/') + "/" + devicePath;
+ return device + "Android/sandbox/" + sandboxId + "/" + devicePath;
} else {
// Does path belong to this sandbox? If so, leave sandbox.
- final String sandboxPrefix = "Android/sandbox/" + sandboxId.replace(':', '/') + "/";
+ final String sandboxPrefix = "Android/sandbox/" + sandboxId + "/";
if (devicePath.startsWith(sandboxPrefix)) {
return device + devicePath.substring(sandboxPrefix.length());
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 591ec00..65f3c03 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -216,6 +216,9 @@
private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ @TelephonyManager.RadioPowerState
+ private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
+
private final LocalLog mLocalLog = new LocalLog(100);
private PreciseDataConnectionState mPreciseDataConnectionState =
@@ -762,6 +765,13 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
+ try {
+ r.callback.onRadioPowerStateChanged(mRadioPowerState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -1609,6 +1619,32 @@
}
}
+ public void notifyRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+ if (!checkNotifyPermission("notifyRadioPowerStateChanged()")) {
+ return;
+ }
+
+ if (VDBG) {
+ log("notifyRadioPowerStateChanged: state= " + state);
+ }
+
+ synchronized (mRecords) {
+ mRadioPowerState = state;
+
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED)) {
+ try {
+ r.callback.onRadioPowerStateChanged(state);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1646,6 +1682,7 @@
pw.println("mVoLteServiceState=" + mVoLteServiceState);
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
+ pw.println("mRadioPowerState=" + mRadioPowerState);
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index b2be5e6..793a177 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1270,26 +1270,36 @@
public int onCommand(String cmd) {
if ("vibrate".equals(cmd)) {
return runVibrate();
+ } else if ("prebaked".equals(cmd)) {
+ return runPrebaked();
}
return handleDefaultCommands(cmd);
}
+ private boolean checkDoNotDisturb() {
+ try {
+ final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ZEN_MODE);
+ if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ pw.print("Ignoring because device is on DND mode ");
+ pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+ zenMode));
+ return true;
+ }
+ }
+ } catch (SettingNotFoundException e) {
+ // ignore
+ }
+
+ return false;
+ }
+
private int runVibrate() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
try {
- try {
- final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ZEN_MODE);
- if (zenMode != Settings.Global.ZEN_MODE_OFF) {
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.print("Ignoring because device is on DND mode ");
- pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
- zenMode));
- return 0;
- }
- }
- } catch (SettingNotFoundException e) {
- // ignore
+ if (checkDoNotDisturb()) {
+ return 0;
}
final long duration = Long.parseLong(getNextArgRequired());
@@ -1311,6 +1321,30 @@
}
}
+ private int runPrebaked() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
+ try {
+ if (checkDoNotDisturb()) {
+ return 0;
+ }
+
+ final int id = Integer.parseInt(getNextArgRequired());
+
+ String description = getNextArg();
+ if (description == null) {
+ description = "Shell command";
+ }
+
+ VibrationEffect effect =
+ VibrationEffect.get(id, false);
+ vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
+ "Shell Command", mToken);
+ return 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
@@ -1321,6 +1355,9 @@
pw.println(" vibrate duration [description]");
pw.println(" Vibrates for duration milliseconds; ignored when device is on DND ");
pw.println(" (Do Not Disturb) mode.");
+ pw.println(" prebaked effect-id [description]");
+ pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
+ pw.println(" (Do Not Disturb) mode.");
pw.println("");
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 0b836f0..9cc550d 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -528,7 +528,7 @@
Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
public void run() {
mActivity.addErrorToDropBox(
- "watchdog", null, "system_server", null, null,
+ "watchdog", null, "system_server", null, null, null,
subject, null, stack, null);
}
};
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index fcda83d..3939bee 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -339,7 +339,8 @@
Slog.w(TAG, uei.getSwitchStatePath() +
" not found while attempting to determine initial switch state");
} catch (Exception e) {
- Slog.e(TAG, "" , e);
+ Slog.e(TAG, "Error while attempting to determine initial switch state for "
+ + uei.getDevName() , e);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8e64b50..4070bca 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -25,7 +25,6 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -552,7 +551,7 @@
if (!callerFg && !fgRequired && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
- if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
+ if (proc == null || proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
// to delay the start if there are already other background services
// that are starting. This is to avoid process start spam when lots
@@ -580,7 +579,7 @@
}
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
addToStarting = true;
- } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+ } else if (proc.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
// We slightly loosen when we will enqueue this new service as a background
// starting service we are waiting for, to also include processes that are
// currently running other services or receivers.
@@ -589,7 +588,7 @@
"Not delaying, but counting as bg: " + r);
} else if (DEBUG_DELAYED_STARTS) {
StringBuilder sb = new StringBuilder(128);
- sb.append("Not potential delay (state=").append(proc.curProcState)
+ sb.append("Not potential delay (state=").append(proc.getCurProcState())
.append(' ').append(proc.adjType);
String reason = proc.makeAdjReason();
if (reason != null) {
@@ -1442,8 +1441,8 @@
}
}
}
- if (anyClientActivities != proc.hasClientActivities) {
- proc.hasClientActivities = anyClientActivities;
+ if (anyClientActivities != proc.hasClientActivities()) {
+ proc.setHasClientActivities(anyClientActivities);
if (updateLru) {
mAm.updateLruProcessLocked(proc, anyClientActivities, null);
}
@@ -1623,8 +1622,9 @@
}
}
- mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
- s.appInfo.uid, s.appInfo.longVersionCode, s.name, s.processName);
+ mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
+ callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
+ s.name, s.processName);
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
mAm.grantEphemeralAccessLocked(callerApp.userId, service,
@@ -1680,7 +1680,7 @@
s.app.whitelistManager = true;
}
// This could have made the service more important.
- mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
+ mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
|| s.app.treatLikeActivity, b.client);
mAm.updateOomAdjLocked(s.app, true);
}
@@ -1794,7 +1794,7 @@
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
r.binding.service.app.treatLikeActivity = true;
mAm.updateLruProcessLocked(r.binding.service.app,
- r.binding.service.app.hasClientActivities
+ r.binding.service.app.hasClientActivities()
|| r.binding.service.app.treatLikeActivity, null);
}
mAm.updateOomAdjLocked(r.binding.service.app, false);
@@ -3259,9 +3259,9 @@
}
}
- void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) {
+ void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
ArrayList<ServiceRecord> services = new ArrayList<>();
- ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(tr.userId);
+ ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(userId);
for (int i = alls.size() - 1; i >= 0; i--) {
ServiceRecord sr = alls.valueAt(i);
if (sr.packageName.equals(component.getPackageName())) {
@@ -3620,7 +3620,7 @@
nextTime = sr.executingStart;
}
}
- if (timeout != null && mAm.mLruProcesses.contains(proc)) {
+ if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
@@ -3641,7 +3641,7 @@
}
if (anrMessage != null) {
- mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
+ proc.appNotResponding(null, null, null, null, false, anrMessage);
}
}
@@ -3666,7 +3666,7 @@
}
if (app != null) {
- mAm.mAppErrors.appNotResponding(app, null, null, false,
+ app.appNotResponding(null, null, null, null, false,
"Context.startForegroundService() did not then call Service.startForeground(): "
+ r);
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 95a8e2a..ede13ef 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -37,16 +37,16 @@
import static com.android.server.am.ActivityDisplayProto.ID;
import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
import static com.android.server.am.ActivityDisplayProto.STACKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -73,7 +73,7 @@
*/
class ActivityDisplay extends ConfigurationContainer<ActivityStack>
implements WindowContainerListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
static final int POSITION_TOP = Integer.MAX_VALUE;
@@ -873,6 +873,52 @@
return null;
}
+ ActivityRecord topRunningActivity() {
+ return topRunningActivity(false /* considerKeyguardState */);
+ }
+
+ /**
+ * Returns the top running activity in the focused stack. In the case the focused stack has no
+ * such activity, the next focusable stack on this display is returned.
+ *
+ * @param considerKeyguardState Indicates whether the locked state should be considered. if
+ * {@code true} and the keyguard is locked, only activities that
+ * can be shown on top of the keyguard will be considered.
+ * @return The top running activity. {@code null} if none is available.
+ */
+ ActivityRecord topRunningActivity(boolean considerKeyguardState) {
+ ActivityRecord topRunning = null;
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ topRunning = focusedStack.topRunningActivityLocked();
+ }
+
+ // Look in other focusable stacks.
+ if (topRunning == null) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ // Only consider focusable stacks other than the current focused one.
+ if (stack == focusedStack || !stack.isFocusable()) {
+ continue;
+ }
+ topRunning = stack.topRunningActivityLocked();
+ if (topRunning != null) {
+ break;
+ }
+ }
+ }
+
+ // This activity can be considered the top running activity if we are not considering
+ // the locked state, the keyguard isn't locked, or we can show when locked.
+ if (topRunning != null && considerKeyguardState
+ && mSupervisor.getKeyguardController().isKeyguardLocked()
+ && !topRunning.canShowWhenLocked()) {
+ return null;
+ }
+
+ return topRunning;
+ }
+
int getIndexOf(ActivityStack stack) {
return mStacks.indexOf(stack);
}
@@ -998,7 +1044,10 @@
* @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
*/
boolean supportsSystemDecorations() {
- return mDisplay.supportsSystemDecorations();
+ return mDisplay.supportsSystemDecorations()
+ // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
+ // (b/114338689) whenever vr 2d display id is set.
+ || mDisplayId == mSupervisor.mService.mVr2dDisplayId;
}
private boolean shouldDestroyContentOnRemove() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3a0289c..5c77f0a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -220,7 +220,7 @@
// Indicates whether the activity starts logging is enabled.
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
- boolean mFlagActivityStartsLoggingEnabled;
+ volatile boolean mFlagActivityStartsLoggingEnabled;
private final ActivityManagerService mService;
private ContentResolver mResolver;
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 0e63d0c..0aaea2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -41,97 +41,48 @@
// Enable all debug log categories.
static final boolean DEBUG_ALL = false;
- // Enable all debug log categories for activities.
- static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
-
// Available log categories in the activity manager package.
- static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_ANR = true; // STOPSHIP disable it (b/113252928)
- static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_BACKGROUND_CHECK = DEBUG_ALL || false;
static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false;
static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
- static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;
- static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
- static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
- static final boolean DEBUG_FOCUS = false;
- static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
- static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
- static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
static final boolean DEBUG_OOM_ADJ_REASON = DEBUG_ALL || false;
- static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
static final boolean DEBUG_POWER = DEBUG_ALL || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
static final boolean DEBUG_PROCESSES = DEBUG_ALL || false;
static final boolean DEBUG_PROVIDER = DEBUG_ALL || false;
static final boolean DEBUG_PSS = DEBUG_ALL || false;
- static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
- static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
- static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
- static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
- static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
- static final boolean DEBUG_STACK = DEBUG_ALL || false;
- static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
- static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
- static final boolean DEBUG_TASKS = DEBUG_ALL || false;
- static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
- static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
- static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
- static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false;
- static final boolean DEBUG_METRICS = DEBUG_ALL || false;
- static final String POSTFIX_ADD_REMOVE = (APPEND_CATEGORY_NAME) ? "_AddRemove" : "";
- static final String POSTFIX_APP = (APPEND_CATEGORY_NAME) ? "_App" : "";
static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : "";
static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : "";
static final String POSTFIX_CLEANUP = (APPEND_CATEGORY_NAME) ? "_Cleanup" : "";
- static final String POSTFIX_CONFIGURATION = (APPEND_CATEGORY_NAME) ? "_Configuration" : "";
- static final String POSTFIX_CONTAINERS = (APPEND_CATEGORY_NAME) ? "_Containers" : "";
- static final String POSTFIX_FOCUS = (APPEND_CATEGORY_NAME) ? "_Focus" : "";
- static final String POSTFIX_IDLE = (APPEND_CATEGORY_NAME) ? "_Idle" : "";
- static final String POSTFIX_IMMERSIVE = (APPEND_CATEGORY_NAME) ? "_Immersive" : "";
- static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : "";
static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : "";
static final String POSTFIX_MU = "_MU";
static final String POSTFIX_NETWORK = "_Network";
static final String POSTFIX_OOM_ADJ = (APPEND_CATEGORY_NAME) ? "_OomAdj" : "";
- static final String POSTFIX_PAUSE = (APPEND_CATEGORY_NAME) ? "_Pause" : "";
static final String POSTFIX_POWER = (APPEND_CATEGORY_NAME) ? "_Power" : "";
static final String POSTFIX_PROCESS_OBSERVERS = (APPEND_CATEGORY_NAME)
? "_ProcessObservers" : "";
static final String POSTFIX_PROCESSES = (APPEND_CATEGORY_NAME) ? "_Processes" : "";
static final String POSTFIX_PROVIDER = (APPEND_CATEGORY_NAME) ? "_Provider" : "";
static final String POSTFIX_PSS = (APPEND_CATEGORY_NAME) ? "_Pss" : "";
- static final String POSTFIX_RECENTS = (APPEND_CATEGORY_NAME) ? "_Recents" : "";
- static final String POSTFIX_RELEASE = (APPEND_CATEGORY_NAME) ? "_Release" : "";
- static final String POSTFIX_RESULTS = (APPEND_CATEGORY_NAME) ? "_Results" : "";
- static final String POSTFIX_SAVED_STATE = (APPEND_CATEGORY_NAME) ? "_SavedState" : "";
static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
static final String POSTFIX_SERVICE_EXECUTING =
(APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : "";
- static final String POSTFIX_STACK = (APPEND_CATEGORY_NAME) ? "_Stack" : "";
- static final String POSTFIX_STATES = (APPEND_CATEGORY_NAME) ? "_States" : "";
- static final String POSTFIX_SWITCH = (APPEND_CATEGORY_NAME) ? "_Switch" : "";
- static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
- static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
? "_UidObservers" : "";
- static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
- static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
- static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
-
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index acf7a73..4eb964a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,19 +18,18 @@
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.REMOVE_TASKS;
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.app.AppOpsManager.OP_NONE;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -49,8 +48,6 @@
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.Process.BLUETOOTH_UID;
import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.Process.FIRST_ISOLATED_UID;
-import static android.os.Process.LAST_ISOLATED_UID;
import static android.os.Process.NFC_UID;
import static android.os.Process.PHONE_UID;
import static android.os.Process.PROC_CHAR;
@@ -69,10 +66,10 @@
import static android.os.Process.THREAD_GROUP_DEFAULT;
import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
-import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-import static android.os.Process.getFreeMemory;
+import static android.os.Process.getPidsForCommands;
import static android.os.Process.getTotalMemory;
+import static android.os.Process.getUidForPid;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
import static android.os.Process.killProcessQuiet;
@@ -84,14 +81,12 @@
import static android.os.Process.setProcessGroup;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
-import static android.os.Process.startWebView;
import static android.os.Process.zygoteProcess;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
@@ -100,9 +95,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
@@ -115,15 +107,12 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_NETWORK;
@@ -134,13 +123,32 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_SHORT_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_CONTAINERS_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_LASTANR_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_LASTANR_TRACES_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_RECENTS_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_STARTER_CMD;
+import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.am.ActivityTaskManagerService.relaunchReasonToString;
+import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
+import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
import android.Manifest;
import android.Manifest.permission;
@@ -153,7 +161,6 @@
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerProto;
-import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -182,6 +189,7 @@
import android.app.PendingIntent;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
+import android.app.WaitResult;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
import android.app.backup.IBackupManager;
@@ -202,7 +210,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy;
-import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
@@ -252,7 +259,6 @@
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.Process;
-import android.os.Process.ProcessStartResult;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -267,9 +273,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
-import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
-import android.os.storage.StorageManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -279,7 +283,6 @@
import android.util.DebugUtils;
import android.util.EventLog;
import android.util.Log;
-import android.util.LongSparseArray;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -315,7 +318,6 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
-import com.android.internal.os.Zygote;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -341,13 +343,11 @@
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.dex.DexManager;
import com.android.server.uri.GrantUri;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.PriorityDump;
@@ -359,18 +359,15 @@
import libcore.util.EmptyArray;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
-import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -399,24 +396,24 @@
*/
public static final int TOP_APP_PRIORITY_BOOST = -10;
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
+ static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
- private static final String TAG_LRU = TAG + POSTFIX_LRU;
+ static final String TAG_LRU = TAG + POSTFIX_LRU;
private static final String TAG_MU = TAG + POSTFIX_MU;
private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
private static final String TAG_POWER = TAG + POSTFIX_POWER;
private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
- private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
+ static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
- private static final String TAG_PSS = TAG + POSTFIX_PSS;
+ static final String TAG_PSS = TAG + POSTFIX_PSS;
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
- private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
+ static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
// Mock "pretend we're idle now" broadcast action to the job scheduler; declared
// here so that while the job scheduler can depend on AMS, the other way around
@@ -434,11 +431,11 @@
static final boolean MONITOR_THREAD_CPU_USAGE = false;
// The flags that are set for all calls we make to the package manager.
- static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
+ public static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
- private static final String ANR_TRACE_DIR = "/data/anr";
+ public static final String ANR_TRACE_DIR = "/data/anr";
// Maximum number of receivers an app can register.
private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
@@ -450,6 +447,12 @@
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+ /**
+ * How long we wait for an provider to be published. Should be longer than
+ * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
+ */
+ static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
+
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
@@ -460,11 +463,7 @@
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
- // Disable hidden API checks for the newly started instrumentation.
- // Must be kept in sync with Am.
- private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
-
- static final int MY_PID = myPid();
+ public static final int MY_PID = myPid();
static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -478,16 +477,13 @@
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
// Necessary ApplicationInfo flags to mark an app as persistent
- private static final int PERSISTENT_MASK =
+ static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
// Intent sent when remote bugreport collection has been completed
private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
- // Used to indicate that an app transition should be animated.
- static final boolean ANIMATE = true;
-
// If set, we will push process association information in to procstats.
static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
@@ -496,6 +492,9 @@
*/
private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 200; // 0.2 sec
+ // The minimum memory growth threshold (in KB) for low RAM devices.
+ private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
+
/**
* State indicating that there is no need for any blocking for network.
*/
@@ -525,9 +524,6 @@
private Installer mInstaller;
- /** Run all ActivityStacks through this */
- ActivityStackSupervisor mStackSupervisor;
-
final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>();
@@ -567,12 +563,6 @@
final AppErrors mAppErrors;
/**
- * Dump of the activity state at the time of the last ANR. Cleared after
- * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
- */
- String mLastANRState;
-
- /**
* Indicates the maximum time spent waiting for the network rules to get updated.
*/
@VisibleForTesting
@@ -625,46 +615,12 @@
final ProcessList mProcessList = new ProcessList();
/**
- * All of the applications we currently have running organized by name.
- * The keys are strings of the application package name (as
- * returned by the package manager), and the keys are ApplicationRecord
- * objects.
- */
- final MyProcessMap mProcessNames = new MyProcessMap();
- final class MyProcessMap extends ProcessMap<ProcessRecord> {
- @Override
- public ProcessRecord put(String name, int uid, ProcessRecord value) {
- final ProcessRecord r = super.put(name, uid, value);
- mAtmInternal.onProcessAdded(r.getWindowProcessController());
- return r;
- }
-
- @Override
- public ProcessRecord remove(String name, int uid) {
- final ProcessRecord r = super.remove(name, uid);
- mAtmInternal.onProcessRemoved(name, uid);
- return r;
- }
- }
-
- /**
* Tracking long-term execution of processes to look for abuse and other
* bad app behavior.
*/
final ProcessStatsService mProcessStats;
/**
- * The currently running isolated processes.
- */
- final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
-
- /**
- * Counter for assigning isolated process uids, to avoid frequently reusing the
- * same ones.
- */
- int mNextIsolatedProcessUid = 0;
-
- /**
* Non-persistent appId whitelist for background restrictions
*/
int[] mBackgroundAppIdWhitelist = new int[] {
@@ -765,28 +721,6 @@
final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
/**
- * Processes that are being forcibly torn down.
- */
- final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
-
- /**
- * List of running applications, sorted by recent usage.
- * The first entry in the list is the least recently used.
- */
- final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
-
- /**
- * Where in mLruProcesses that the processes hosting activities start.
- */
- int mLruProcessActivityStart = 0;
-
- /**
- * Where in mLruProcesses that the processes hosting services start.
- * This is after (lower index) than mLruProcessesActivityStart.
- */
- int mLruProcessServiceStart = 0;
-
- /**
* List of processes that should gc as soon as things are idle.
*/
final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
@@ -1053,11 +987,6 @@
final AppOpsService mAppOpsService;
/**
- * Hardware-reported OpenGLES version.
- */
- final int GL_ES_VERSION;
-
- /**
* List of initialization arguments to pass to all processes when binding applications to them.
* For example, references to the commonly used services.
*/
@@ -1077,7 +1006,6 @@
@GuardedBy("this") boolean mCallFinishBooting = false;
@GuardedBy("this") boolean mBootAnimationComplete = false;
- private @GuardedBy("this") boolean mCheckedForSetup = false;
final Context mContext;
@@ -1108,11 +1036,6 @@
int mAdjSeq = 0;
/**
- * Current sequence id for process LRU updating.
- */
- int mLruSeq = 0;
-
- /**
* Keep track of the non-cached/empty process we last found, to help
* determine how to distribute cached/empty processes next time.
*/
@@ -1224,31 +1147,6 @@
private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
- /**
- * A global counter for generating sequence numbers.
- * This value will be used when incrementing sequence numbers in individual uidRecords.
- *
- * Having a global counter ensures that seq numbers are monotonically increasing for a
- * particular uid even when the uidRecord is re-created.
- */
- @GuardedBy("this")
- @VisibleForTesting
- long mProcStateSeqCounter = 0;
-
- /**
- * A global counter for generating sequence numbers to uniquely identify pending process starts.
- */
- @GuardedBy("this")
- private long mProcStartSeqCounter = 0;
-
- /**
- * Contains {@link ProcessRecord} objects for pending process starts.
- *
- * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
- */
- @GuardedBy("this")
- private final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
-
private final Injector mInjector;
static final class ProcessChangeItem {
@@ -1405,7 +1303,6 @@
static final int SHOW_ERROR_UI_MSG = 1;
static final int SHOW_NOT_RESPONDING_UI_MSG = 2;
- static final int UPDATE_CONFIGURATION_MSG = 4;
static final int GC_BACKGROUND_PROCESSES_MSG = 5;
static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
static final int SERVICE_TIMEOUT_MSG = 12;
@@ -1420,7 +1317,6 @@
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
- static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
static final int DELETE_DUMPHEAP_MSG = 51;
@@ -1434,16 +1330,10 @@
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
- static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
- static final int FIRST_COMPAT_MODE_MSG = 300;
- static final int FIRST_SUPERVISOR_STACK_MSG = 100;
static final String SERVICE_RECORD_KEY = "servicerecord";
- static ServiceThread sKillThread = null;
- static KillHandler sKillHandler = null;
-
long mLastMemUsageReportTime = 0;
/**
@@ -1477,31 +1367,7 @@
* also corresponds to the merged configuration of the default display.
*/
Configuration getGlobalConfiguration() {
- return mStackSupervisor.getConfiguration();
- }
-
- final class KillHandler extends Handler {
- static final int KILL_PROCESS_GROUP_MSG = 4000;
-
- public KillHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case KILL_PROCESS_GROUP_MSG:
- {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
- Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- break;
-
- default:
- super.handleMessage(msg);
- }
- }
+ return mActivityTaskManager.getGlobalConfiguration();
}
final class UiHandler extends Handler {
@@ -1600,11 +1466,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case UPDATE_CONFIGURATION_MSG: {
- final ContentResolver resolver = mContext.getContentResolver();
- Settings.System.putConfigurationForUser(resolver, (Configuration) msg.obj,
- msg.arg1);
- } break;
case GC_BACKGROUND_PROCESSES_MSG: {
synchronized (ActivityManagerService.this) {
performAppGcsIfAppropriateLocked();
@@ -1622,8 +1483,8 @@
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mProcessList.mLruProcesses.get(i);
if (r.thread != null) {
try {
r.thread.updateTimeZone();
@@ -1636,16 +1497,7 @@
} break;
case CLEAR_DNS_CACHE_MSG: {
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
- try {
- r.thread.clearDnsCache();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
- }
- }
- }
+ mProcessList.clearAllDnsCacheLocked();
}
} break;
case UPDATE_HTTP_PROXY_MSG: {
@@ -1661,19 +1513,7 @@
pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- // Don't dispatch to isolated processes as they can't access
- // ConnectivityManager and don't have network privileges anyway.
- if (r.thread != null && !r.isolated) {
- try {
- r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to update http proxy for: " +
- r.info.processName);
- }
- }
- }
+ mProcessList.setAllHttpProxyLocked(host, port, exclList, pacFileUrl);
}
} break;
case PROC_START_TIMEOUT_MSG: {
@@ -1721,29 +1561,7 @@
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
- try {
- r.thread.updateTimePrefs(msg.arg1);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to update preferences for: "
- + r.info.processName);
- }
- }
- }
- }
- break;
- }
- case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
- try {
- Locale l = (Locale) msg.obj;
- IBinder service = ServiceManager.getService("mount");
- IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
- Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
- storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
- } catch (RemoteException e) {
- Log.e(TAG, "Error storing locale for decryption UI", e);
+ mProcessList.updateAllTimePrefsLocked(msg.arg1);
}
break;
}
@@ -1867,17 +1685,7 @@
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
- try {
- r.thread.handleTrustStorageUpdate();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to handle trust storage update for: " +
- r.info.processName);
- }
- }
- }
+ mProcessList.handleAllTrustStorageUpdateLocked();
}
} break;
}
@@ -2017,7 +1825,9 @@
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
- ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
+ ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
+ false,
+ 0);
app.setPersistent(true);
app.pid = MY_PID;
app.getWindowProcessController().setPid(MY_PID);
@@ -2026,7 +1836,7 @@
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
- updateLruProcessLocked(app, false, null);
+ mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
} catch (PackageManager.NameNotFoundException e) {
@@ -2052,12 +1862,12 @@
synchronized (this) {
mWindowManager = wm;
mActivityTaskManager.setWindowManager(wm);
- mStackSupervisor.setWindowManager(wm);
}
}
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
mUsageStatsService = usageStatsManager;
+ mActivityTaskManager.setUsageStatsManager(usageStatsManager);
}
public void startObservingNativeCrashes() {
@@ -2294,25 +2104,38 @@
@VisibleForTesting
public ActivityManagerService(Injector injector) {
+ this(injector, null /* handlerThread */);
+ }
+
+ /**
+ * Provides the basic functionality for activity task related tests when a handler thread is
+ * given to initialize the dependency members.
+ */
+ @VisibleForTesting
+ ActivityManagerService(Injector injector, ServiceThread handlerThread) {
+ final boolean hasHandlerThread = handlerThread != null;
mInjector = injector;
mContext = mInjector.getContext();
mUiContext = null;
- GL_ES_VERSION = 0;
mAppErrors = null;
- mAppOpsService = mInjector.getAppOpsService(null, null);
+ mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
mBatteryStatsService = null;
- mConstants = null;
- mHandler = null;
- mHandlerThread = null;
- mIntentFirewall = null;
+ mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
+ mHandlerThread = handlerThread;
+ mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+ mIntentFirewall = hasHandlerThread
+ ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
mProcessCpuThread = null;
mProcessStats = null;
mProviderMap = null;
- mServices = null;
+ // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
+ // {@link ActivityStackSupervisor#cleanUpRemovedTaskLocked}.
+ mServices = hasHandlerThread ? new ActiveServices(this) : null;
mSystemThread = null;
- mUiHandler = injector.getUiHandler(null);
- mUserController = null;
- mPendingIntentController = null;
+ mUiHandler = injector.getUiHandler(null /* service */);
+ mUserController = hasHandlerThread ? new UserController(this) : null;
+ mPendingIntentController = hasHandlerThread
+ ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
@@ -2345,13 +2168,7 @@
mConstants = new ActivityManagerConstants(this, mHandler);
- /* static; one-time init here */
- if (sKillHandler == null) {
- sKillThread = new ServiceThread(TAG + ":kill",
- THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
- sKillThread.start();
- sKillHandler = new KillHandler(sKillThread.getLooper());
- }
+ mProcessList.init(this);
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
@@ -2387,9 +2204,6 @@
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController);
- GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
- ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
-
if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
mUseFifoUiScheduling = true;
}
@@ -2398,9 +2212,9 @@
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mActivityTaskManager = atm;
- mActivityTaskManager.setActivityManagerService(this);
+ mActivityTaskManager.setActivityManagerService(this, mHandlerThread.getLooper(),
+ mIntentFirewall, mPendingIntentController);
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
- mStackSupervisor = mActivityTaskManager.mStackSupervisor;
mProcessCpuThread = new Thread("CpuTracker") {
@Override
@@ -2504,12 +2318,13 @@
if (code == SYSPROPS_TRANSACTION) {
// We need to tell all apps about the system property change.
ArrayList<IBinder> procs = new ArrayList<IBinder>();
- synchronized(this) {
- final int NP = mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ synchronized (this) {
+ final int NP = mProcessList.mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ SparseArray<ProcessRecord> apps =
+ mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
+ for (int ia = 0; ia < NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
if (app.thread != null) {
procs.add(app.thread.asBinder());
@@ -2759,329 +2574,21 @@
mActivityTaskManager.unregisterTaskStackListener(listener);
}
- private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
- String what, Object obj, ProcessRecord srcApp) {
- app.lastActivityTime = now;
-
- if (app.hasActivitiesOrRecentTasks()) {
- // Don't want to touch dependent processes that are hosting activities.
- return index;
- }
-
- int lrui = mLruProcesses.lastIndexOf(app);
- if (lrui < 0) {
- Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
- + what + " " + obj + " from " + srcApp);
- return index;
- }
-
- if (lrui >= index) {
- // Don't want to cause this to move dependent processes *back* in the
- // list as if they were less frequently used.
- return index;
- }
-
- if (lrui >= mLruProcessActivityStart) {
- // Don't want to touch dependent processes that are hosting activities.
- return index;
- }
-
- mLruProcesses.remove(lrui);
- if (index > 0) {
- index--;
- }
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
- + " in LRU list: " + app);
- mLruProcesses.add(index, app);
- return index;
- }
-
- static void killProcessGroup(int uid, int pid) {
- if (sKillHandler != null) {
- sKillHandler.sendMessage(
- sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
- } else {
- Slog.w(TAG, "Asked to kill process group before system bringup!");
- Process.killProcessGroup(uid, pid);
- }
+ final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+ ProcessRecord client) {
+ mProcessList.updateLruProcessLocked(app, activityChange, client);
}
final void removeLruProcessLocked(ProcessRecord app) {
- int lrui = mLruProcesses.lastIndexOf(app);
- if (lrui >= 0) {
- if (!app.killed) {
- if (app.isPersistent()) {
- Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
- } else {
- Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
- if (app.pid > 0) {
- killProcessQuiet(app.pid);
- killProcessGroup(app.uid, app.pid);
- } else {
- app.pendingStart = false;
- }
- }
- }
- if (lrui <= mLruProcessActivityStart) {
- mLruProcessActivityStart--;
- }
- if (lrui <= mLruProcessServiceStart) {
- mLruProcessServiceStart--;
- }
- mLruProcesses.remove(lrui);
- }
- }
-
- final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
- ProcessRecord client) {
- final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities
- || app.treatLikeActivity;
- final boolean hasService = false; // not impl yet. app.services.size() > 0;
- if (!activityChange && hasActivity) {
- // The process has activities, so we are only allowing activity-based adjustments
- // to move it. It should be kept in the front of the list with other
- // processes that have activities, and we don't want those to change their
- // order except due to activity operations.
- return;
- }
-
- mLruSeq++;
- final long now = SystemClock.uptimeMillis();
- app.lastActivityTime = now;
-
- // First a quick reject: if the app is already at the position we will
- // put it, then there is nothing to do.
- if (hasActivity) {
- final int N = mLruProcesses.size();
- if (N > 0 && mLruProcesses.get(N-1) == app) {
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
- return;
- }
- } else {
- if (mLruProcessServiceStart > 0
- && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
- return;
- }
- }
-
- int lrui = mLruProcesses.lastIndexOf(app);
-
- if (app.isPersistent() && lrui >= 0) {
- // We don't care about the position of persistent processes, as long as
- // they are in the list.
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
- return;
- }
-
- /* In progress: compute new position first, so we can avoid doing work
- if the process is not actually going to move. Not yet working.
- int addIndex;
- int nextIndex;
- boolean inActivity = false, inService = false;
- if (hasActivity) {
- // Process has activities, put it at the very tipsy-top.
- addIndex = mLruProcesses.size();
- nextIndex = mLruProcessServiceStart;
- inActivity = true;
- } else if (hasService) {
- // Process has services, put it at the top of the service list.
- addIndex = mLruProcessActivityStart;
- nextIndex = mLruProcessServiceStart;
- inActivity = true;
- inService = true;
- } else {
- // Process not otherwise of interest, it goes to the top of the non-service area.
- addIndex = mLruProcessServiceStart;
- if (client != null) {
- int clientIndex = mLruProcesses.lastIndexOf(client);
- if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
- + app);
- if (clientIndex >= 0 && addIndex > clientIndex) {
- addIndex = clientIndex;
- }
- }
- nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
- }
-
- Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
- + mLruProcessActivityStart + "): " + app);
- */
-
- if (lrui >= 0) {
- if (lrui < mLruProcessActivityStart) {
- mLruProcessActivityStart--;
- }
- if (lrui < mLruProcessServiceStart) {
- mLruProcessServiceStart--;
- }
- /*
- if (addIndex > lrui) {
- addIndex--;
- }
- if (nextIndex > lrui) {
- nextIndex--;
- }
- */
- mLruProcesses.remove(lrui);
- }
-
- /*
- mLruProcesses.add(addIndex, app);
- if (inActivity) {
- mLruProcessActivityStart++;
- }
- if (inService) {
- mLruProcessActivityStart++;
- }
- */
-
- int nextIndex;
- if (hasActivity) {
- final int N = mLruProcesses.size();
- if ((!app.hasActivities() || app.hasRecentTasks())
- && mLruProcessActivityStart < (N - 1)) {
- // Process doesn't have activities, but has clients with
- // activities... move it up, but one below the top (the top
- // should always have a real activity).
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Adding to second-top of LRU activity list: " + app);
- mLruProcesses.add(N - 1, app);
- // To keep it from spamming the LRU list (by making a bunch of clients),
- // we will push down any other entries owned by the app.
- final int uid = app.info.uid;
- for (int i = N - 2; i > mLruProcessActivityStart; i--) {
- ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid == uid) {
- // We want to push this one down the list. If the process after
- // it is for the same uid, however, don't do so, because we don't
- // want them internally to be re-ordered.
- if (mLruProcesses.get(i - 1).info.uid != uid) {
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Pushing uid " + uid + " swapping at " + i + ": "
- + mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
- ProcessRecord tmp = mLruProcesses.get(i);
- mLruProcesses.set(i, mLruProcesses.get(i - 1));
- mLruProcesses.set(i - 1, tmp);
- i--;
- }
- } else {
- // A gap, we can stop here.
- break;
- }
- }
- } else {
- // Process has activities, put it at the very tipsy-top.
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
- mLruProcesses.add(app);
- }
- nextIndex = mLruProcessServiceStart;
- } else if (hasService) {
- // Process has services, put it at the top of the service list.
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
- mLruProcesses.add(mLruProcessActivityStart, app);
- nextIndex = mLruProcessServiceStart;
- mLruProcessActivityStart++;
- } else {
- // Process not otherwise of interest, it goes to the top of the non-service area.
- int index = mLruProcessServiceStart;
- if (client != null) {
- // If there is a client, don't allow the process to be moved up higher
- // in the list than that client.
- int clientIndex = mLruProcesses.lastIndexOf(client);
- if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
- + " when updating " + app);
- if (clientIndex <= lrui) {
- // Don't allow the client index restriction to push it down farther in the
- // list than it already is.
- clientIndex = lrui;
- }
- if (clientIndex >= 0 && index > clientIndex) {
- index = clientIndex;
- }
- }
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
- mLruProcesses.add(index, app);
- nextIndex = index-1;
- mLruProcessActivityStart++;
- mLruProcessServiceStart++;
- }
-
- // If the app is currently using a content provider or service,
- // bump those processes as well.
- for (int j=app.connections.size()-1; j>=0; j--) {
- ConnectionRecord cr = app.connections.valueAt(j);
- if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
- && cr.binding.service.app != null
- && cr.binding.service.app.lruSeq != mLruSeq
- && !cr.binding.service.app.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
- "service connection", cr, app);
- }
- }
- for (int j=app.conProviders.size()-1; j>=0; j--) {
- ContentProviderRecord cpr = app.conProviders.get(j).provider;
- if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
- "provider reference", cpr, app);
- }
- }
+ mProcessList.removeLruProcessLocked(app);
}
final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
- if (uid == SYSTEM_UID) {
- // The system gets to run in any process. If there are multiple
- // processes with the same uid, just pick the first (this
- // should never happen).
- SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
- if (procs == null) return null;
- final int procCount = procs.size();
- for (int i = 0; i < procCount; i++) {
- final int procUid = procs.keyAt(i);
- if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
- // Don't use an app process or different user process for system component.
- continue;
- }
- return procs.valueAt(i);
- }
- }
- ProcessRecord proc = mProcessNames.get(processName, uid);
- if (false && proc != null && !keepIfLarge
- && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
- && proc.lastCachedPss >= 4000) {
- // Turn this condition on to cause killing to happen regularly, for testing.
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- proc.lastCachedPss, holder.appVersion);
- }
- }
- proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
- } else if (proc != null && !keepIfLarge
- && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
- && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
- if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- proc.lastCachedPss, holder.appVersion);
- }
- }
- proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
- }
- }
- return proc;
+ return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
+ }
+
+ final ProcessMap<ProcessRecord> getProcessNames() {
+ return mProcessList.mProcessNames;
}
void notifyPackageUse(String packageName, int reason) {
@@ -3108,7 +2615,7 @@
info.packageName = "android";
info.seInfoUser = SELinuxUtil.COMPLETE_STR;
info.targetSdkVersion = Build.VERSION.SDK_INT;
- ProcessRecord proc = startProcessLocked(processName, info /* info */,
+ ProcessRecord proc = mProcessList.startProcessLocked(processName, info /* info */,
false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */,
null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
@@ -3122,548 +2629,17 @@
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
- return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
+ return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
+ hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
- @GuardedBy("this")
- final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
- boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
- boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
- String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
- long startTime = SystemClock.elapsedRealtime();
- ProcessRecord app;
- if (!isolated) {
- app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
- checkTime(startTime, "startProcess: after getProcessRecord");
-
- if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
- // If we are in the background, then check to see if this process
- // is bad. If so, we will just silently fail.
- if (mAppErrors.isBadProcessLocked(info)) {
- if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
- + "/" + info.processName);
- return null;
- }
- } else {
- // When the user is explicitly starting a process, then clear its
- // crash count so that we won't make it bad until they see at
- // least one crash dialog again, and make the process good again
- // if it had been bad.
- if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
- + "/" + info.processName);
- mAppErrors.resetProcessCrashTimeLocked(info);
- if (mAppErrors.isBadProcessLocked(info)) {
- EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
- UserHandle.getUserId(info.uid), info.uid,
- info.processName);
- mAppErrors.clearBadProcessLocked(info);
- if (app != null) {
- app.bad = false;
- }
- }
- }
- } else {
- // If this is an isolated process, it can't re-use an existing process.
- app = null;
- }
-
- // We don't have to do anything more if:
- // (1) There is an existing application record; and
- // (2) The caller doesn't think it is dead, OR there is no thread
- // object attached to it so we know it couldn't have crashed; and
- // (3) There is a pid assigned to it, so it is either starting or
- // already running.
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
- + " app=" + app + " knownToBeDead=" + knownToBeDead
- + " thread=" + (app != null ? app.thread : null)
- + " pid=" + (app != null ? app.pid : -1));
- if (app != null && app.pid > 0) {
- if ((!knownToBeDead && !app.killed) || app.thread == null) {
- // We already have the app running, or are waiting for it to
- // come up (we have a pid but not yet its thread), so keep it.
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
- // If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, info.versionCode, mProcessStats);
- checkTime(startTime, "startProcess: done, added package to proc");
- return app;
- }
-
- // An application record is attached to a previous process,
- // clean it up now.
- if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
- checkTime(startTime, "startProcess: bad proc running, killing");
- killProcessGroup(app.uid, app.pid);
- handleAppDiedLocked(app, true, true);
- checkTime(startTime, "startProcess: done killing old proc");
- }
-
- String hostingNameStr = hostingName != null
- ? hostingName.flattenToShortString() : null;
-
- if (app == null) {
- checkTime(startTime, "startProcess: creating new process record");
- app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
- if (app == null) {
- Slog.w(TAG, "Failed making new process record for "
- + processName + "/" + info.uid + " isolated=" + isolated);
- return null;
- }
- app.crashHandler = crashHandler;
- app.isolatedEntryPoint = entryPoint;
- app.isolatedEntryPointArgs = entryPointArgs;
- checkTime(startTime, "startProcess: done creating new process record");
- } else {
- // If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, info.versionCode, mProcessStats);
- checkTime(startTime, "startProcess: added package to existing proc");
- }
-
- // If the system is not ready yet, then hold off on starting this
- // process until it is.
- if (!mProcessesReady
- && !isAllowedWhileBooting(info)
- && !allowWhileBooting) {
- if (!mProcessesOnHold.contains(app)) {
- mProcessesOnHold.add(app);
- }
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
- "System not ready, putting on hold: " + app);
- checkTime(startTime, "startProcess: returning with proc on hold");
- return app;
- }
-
- checkTime(startTime, "startProcess: stepping in to startProcess");
- final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
- checkTime(startTime, "startProcess: done starting proc!");
- return success ? app : null;
- }
-
boolean isAllowedWhileBooting(ApplicationInfo ai) {
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
- @GuardedBy("this")
- private final void startProcessLocked(ProcessRecord app,
- String hostingType, String hostingNameStr) {
- startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
- }
-
- @GuardedBy("this")
- private final boolean startProcessLocked(ProcessRecord app,
- String hostingType, String hostingNameStr, String abiOverride) {
- return startProcessLocked(app, hostingType, hostingNameStr,
- false /* disableHiddenApiChecks */, abiOverride);
- }
-
- /**
- * @return {@code true} if process start is successful, false otherwise.
- */
- @GuardedBy("this")
- private final boolean startProcessLocked(ProcessRecord app, String hostingType,
- String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
- if (app.pendingStart) {
- return true;
- }
- long startTime = SystemClock.elapsedRealtime();
- if (app.pid > 0 && app.pid != MY_PID) {
- checkTime(startTime, "startProcess: removing from pids map");
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(app.pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
- checkTime(startTime, "startProcess: done removing from pids map");
- app.setPid(0);
- }
-
- if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
- "startProcessLocked removing on hold: " + app);
- mProcessesOnHold.remove(app);
-
- checkTime(startTime, "startProcess: starting to update cpu stats");
- updateCpuStats();
- checkTime(startTime, "startProcess: done updating cpu stats");
-
- try {
- try {
- final int userId = UserHandle.getUserId(app.uid);
- AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
- int uid = app.uid;
- int[] gids = null;
- int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
- if (!app.isolated) {
- int[] permGids = null;
- try {
- checkTime(startTime, "startProcess: getting gids from package manager");
- final IPackageManager pm = AppGlobals.getPackageManager();
- permGids = pm.getPackageGids(app.info.packageName,
- MATCH_DEBUG_TRIAGED_MISSING, app.userId);
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
- app.info.packageName);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
- /*
- * Add shared application and profile GIDs so applications can share some
- * resources like shared libraries and access user-wide resources
- */
- if (ArrayUtils.isEmpty(permGids)) {
- gids = new int[3];
- } else {
- gids = new int[permGids.length + 3];
- System.arraycopy(permGids, 0, gids, 3, permGids.length);
- }
- gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
- gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
- gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
-
- // Replace any invalid GIDs
- if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
- if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
- }
- checkTime(startTime, "startProcess: building args");
- if (mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
- uid = 0;
- }
- int runtimeFlags = 0;
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
- runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
- // Also turn on CheckJNI for debuggable apps. It's quite
- // awkward to turn on otherwise.
- runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
- }
- // Run the app in safe mode if its manifest requests so or the
- // system is booted in safe mode.
- if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
- mSafeMode == true) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
- }
- if ("1".equals(SystemProperties.get("debug.checkjni"))) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
- }
- String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
- runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
- }
- String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
- if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
- runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
- }
- if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
- }
- if ("1".equals(SystemProperties.get("debug.assert"))) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
- }
- if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
- // Enable all debug flags required by the native debugger.
- runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
- runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
- runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
- mNativeDebuggingApp = null;
- }
-
- if (app.info.isPrivilegedApp() &&
- DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
- runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
- }
-
- if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
- app.info.maybeUpdateHiddenApiEnforcementPolicy(
- mHiddenApiBlacklist.getPolicyForPrePApps(),
- mHiddenApiBlacklist.getPolicyForPApps());
- @HiddenApiEnforcementPolicy int policy =
- app.info.getHiddenApiEnforcementPolicy();
- int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
- if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
- throw new IllegalStateException("Invalid API policy: " + policy);
- }
- runtimeFlags |= policyBits;
- }
-
- String invokeWith = null;
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- // Debuggable apps may include a wrapper script with their library directory.
- String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- try {
- if (new File(wrapperFileName).exists()) {
- invokeWith = "/system/bin/logwrapper " + wrapperFileName;
- }
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
-
- String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
- if (requiredAbi == null) {
- requiredAbi = Build.SUPPORTED_ABIS[0];
- }
-
- String instructionSet = null;
- if (app.info.primaryCpuAbi != null) {
- instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
- }
-
- app.gids = gids;
- app.setRequiredAbi(requiredAbi);
- app.instructionSet = instructionSet;
-
- // the per-user SELinux context must be set
- if (TextUtils.isEmpty(app.info.seInfoUser)) {
- Slog.wtf(TAG, "SELinux tag not defined",
- new IllegalStateException("SELinux tag not defined for "
- + app.info.packageName + " (uid " + app.uid + ")"));
- }
- final String seInfo = app.info.seInfo
- + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
- // Start the process. It will either succeed and return a result containing
- // the PID of the new process, or else throw a RuntimeException.
- final String entryPoint = "android.app.ActivityThread";
-
- return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
- runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
- startTime);
- } catch (RuntimeException e) {
- Slog.e(TAG, "Failure starting process " + app.processName, e);
-
- // Something went very wrong while trying to start this process; one
- // common case is when the package is frozen due to an active
- // upgrade. To recover, clean up any active bookkeeping related to
- // starting this process. (We already invoked this method once when
- // the package was initially frozen through KILL_APPLICATION_MSG, so
- // it doesn't hurt to use it again.)
- forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
- false, true, false, false, app.userId, "start failure");
- return false;
- }
- }
-
- @GuardedBy("this")
- private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
- ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
- String seInfo, String requiredAbi, String instructionSet, String invokeWith,
- long startTime) {
- app.pendingStart = true;
- app.killedByAm = false;
- app.removed = false;
- app.killed = false;
- final long startSeq = app.startSeq = ++mProcStartSeqCounter;
- app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
- if (mConstants.FLAG_PROCESS_START_ASYNC) {
- if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
- "Posting procStart msg for " + app.toShortString());
- mProcStartHandler.post(() -> {
- try {
- synchronized (ActivityManagerService.this) {
- final String reason = isProcStartValidLocked(app, startSeq);
- if (reason != null) {
- Slog.w(TAG_PROCESSES, app + " not valid anymore,"
- + " don't start process, " + reason);
- app.pendingStart = false;
- return;
- }
- app.setUsingWrapper(invokeWith != null
- || SystemProperties.get("wrap." + app.processName) != null);
- mPendingStarts.put(startSeq, app);
- }
- final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
- app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
- requiredAbi, instructionSet, invokeWith, app.startTime);
- synchronized (ActivityManagerService.this) {
- handleProcessStartedLocked(app, startResult, startSeq);
- }
- } catch (RuntimeException e) {
- synchronized (ActivityManagerService.this) {
- Slog.e(TAG, "Failure starting process " + app.processName, e);
- mPendingStarts.remove(startSeq);
- app.pendingStart = false;
- forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
- }
- }
- });
- return true;
- } else {
- try {
- final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
- uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
- invokeWith, startTime);
- handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
- startSeq, false);
- } catch (RuntimeException e) {
- Slog.e(TAG, "Failure starting process " + app.processName, e);
- app.pendingStart = false;
- forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
- }
- return app.pid > 0;
- }
- }
-
- private ProcessStartResult startProcess(String hostingType, String entryPoint,
- ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
- String seInfo, String requiredAbi, String instructionSet, String invokeWith,
- long startTime) {
- try {
- final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
- final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
- .getVisibleVolumesForUser(UserHandle.getUserId(uid));
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
- app.processName);
- checkTime(startTime, "startProcess: asking zygote to start proc");
- final ProcessStartResult startResult;
- if (hostingType.equals("webview_service")) {
- startResult = startWebView(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
- app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, null, app.info.packageName,
- packageNames, visibleVolIds,
- new String[] {PROC_START_SEQ_IDENT + app.startSeq});
- } else {
- startResult = Process.start(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
- app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, invokeWith, app.info.packageName,
- packageNames, visibleVolIds,
- new String[] {PROC_START_SEQ_IDENT + app.startSeq});
- }
- checkTime(startTime, "startProcess: returned from zygote!");
- return startResult;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- }
-
- @GuardedBy("this")
- private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
- StringBuilder sb = null;
- if (app.killedByAm) {
- if (sb == null) sb = new StringBuilder();
- sb.append("killedByAm=true;");
- }
- if (mProcessNames.get(app.processName, app.uid) != app) {
- if (sb == null) sb = new StringBuilder();
- sb.append("No entry in mProcessNames;");
- }
- if (!app.pendingStart) {
- if (sb == null) sb = new StringBuilder();
- sb.append("pendingStart=false;");
- }
- if (app.startSeq > expectedStartSeq) {
- if (sb == null) sb = new StringBuilder();
- sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
- }
- return sb == null ? null : sb.toString();
- }
-
- @GuardedBy("this")
- private boolean handleProcessStartedLocked(ProcessRecord pending,
- ProcessStartResult startResult, long expectedStartSeq) {
- // Indicates that this process start has been taken care of.
- if (mPendingStarts.get(expectedStartSeq) == null) {
- if (pending.pid == startResult.pid) {
- pending.setUsingWrapper(startResult.usingWrapper);
- // TODO: Update already existing clients of usingWrapper
- }
- return false;
- }
- return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
- expectedStartSeq, false);
- }
-
- @GuardedBy("this")
- private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
- long expectedStartSeq, boolean procAttached) {
- mPendingStarts.remove(expectedStartSeq);
- final String reason = isProcStartValidLocked(app, expectedStartSeq);
- if (reason != null) {
- Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
- + ", " + reason);
- app.pendingStart = false;
- Process.killProcessQuiet(pid);
- Process.killProcessGroup(app.uid, app.pid);
- return false;
- }
- mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
- checkTime(app.startTime, "startProcess: done updating battery stats");
-
- EventLog.writeEvent(EventLogTags.AM_PROC_START,
- UserHandle.getUserId(app.startUid), pid, app.startUid,
- app.processName, app.hostingType,
- app.hostingNameStr != null ? app.hostingNameStr : "");
-
- try {
- AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
- app.seInfo, app.info.sourceDir, pid);
- } catch (RemoteException ex) {
- // Ignore
- }
-
- if (app.isPersistent()) {
- Watchdog.getInstance().processStarted(app.processName, pid);
- }
-
- checkTime(app.startTime, "startProcess: building log message");
- StringBuilder buf = mStringBuilder;
- buf.setLength(0);
- buf.append("Start proc ");
- buf.append(pid);
- buf.append(':');
- buf.append(app.processName);
- buf.append('/');
- UserHandle.formatUid(buf, app.startUid);
- if (app.isolatedEntryPoint != null) {
- buf.append(" [");
- buf.append(app.isolatedEntryPoint);
- buf.append("]");
- }
- buf.append(" for ");
- buf.append(app.hostingType);
- if (app.hostingNameStr != null) {
- buf.append(" ");
- buf.append(app.hostingNameStr);
- }
- reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
- app.setPid(pid);
- app.setUsingWrapper(usingWrapper);
- app.pendingStart = false;
- checkTime(app.startTime, "startProcess: starting to update pids map");
- ProcessRecord oldApp;
- synchronized (mPidsSelfLocked) {
- oldApp = mPidsSelfLocked.get(pid);
- }
- // If there is already an app occupying that pid that hasn't been cleaned up
- if (oldApp != null && !app.isolated) {
- // Clean up anything relating to this pid first
- Slog.w(TAG, "Reusing pid " + pid
- + " while app is still mapped to it");
- cleanUpApplicationRecordLocked(oldApp, false, false, -1,
- true /*replacingPid*/);
- }
- synchronized (mPidsSelfLocked) {
- this.mPidsSelfLocked.put(pid, app);
- if (!procAttached) {
- Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
- msg.obj = app;
- mHandler.sendMessageDelayed(msg, usingWrapper
- ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
- }
- }
- checkTime(app.startTime, "startProcess: done updating pids map");
- return true;
- }
-
void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + activity + "res=" + resumed);
@@ -3693,14 +2669,6 @@
}
}
- boolean getCheckedForSetup() {
- return mCheckedForSetup;
- }
-
- void setCheckedForSetup(boolean checked) {
- mCheckedForSetup = checked;
- }
-
CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
return mAtmInternal.compatibilityInfoForPackage(ai);
}
@@ -3735,8 +2703,8 @@
int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
synchronized (this) {
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- final ProcessRecord proc = mLruProcesses.get(i);
+ for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+ final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
if (procState > proc.setProcState) {
if (proc.pkgList.containsKey(packageName) ||
(proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
@@ -3764,7 +2732,7 @@
"Unable to set a higher trim level than current level");
}
if (!(level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN ||
- app.curProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
+ app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
throw new IllegalArgumentException("Unable to set a background trim level "
+ "on a foreground process");
}
@@ -4030,9 +2998,35 @@
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
- return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
- resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
- userId);
+ synchronized (this) {
+ /**
+ * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+ * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+ * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+ * later to modify process settings due to the flags.
+ * TODO(b/80414790): Investigate a better way of untangling this.
+ */
+ return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
+ resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+ bOptions, userId);
+ }
+ }
+
+ WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+ Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+ synchronized (this) {
+ /**
+ * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+ * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+ * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+ * later to modify process settings due to the flags.
+ * TODO(b/80414790): Investigate a better way of untangling this.
+ */
+ return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent,
+ resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+ bOptions, userId);
+ }
}
@Override
@@ -4111,7 +3105,7 @@
* to the process.
*/
@GuardedBy("this")
- private final void handleAppDiedLocked(ProcessRecord app,
+ final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
@@ -4127,45 +3121,13 @@
clearProfilerLocked();
}
- // Remove this application's activities from active lists.
- boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app.getWindowProcessController());
-
- app.clearRecentTasks();
- app.clearActivities();
-
- if (app.getActiveInstrumentation() != null) {
+ mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
Slog.w(TAG, "Crash of app " + app.processName
- + " running instrumentation " + app.getActiveInstrumentation().mClass);
+ + " running instrumentation " + app.getActiveInstrumentation().mClass);
Bundle info = new Bundle();
info.putString("shortMsg", "Process crashed.");
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
- }
-
- mWindowManager.deferSurfaceLayout();
- try {
- if (!restarting && hasVisibleActivities
- && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
- // If there was nothing to resume, and we are not already restarting this process, but
- // there is a visible activity that is hosted by the process... then make sure all
- // visible activities are running, taking care of restarting this process.
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- }
- } finally {
- mWindowManager.continueSurfaceLayout();
- }
-
- }
-
- private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
- final IBinder threadBinder = thread.asBinder();
- // Find the application record.
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- final ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
- return i;
- }
- }
- return -1;
+ });
}
ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
@@ -4173,15 +3135,14 @@
return null;
}
- int appIndex = getLRURecordIndexForAppLocked(thread);
- if (appIndex >= 0) {
- return mLruProcesses.get(appIndex);
- }
+ ProcessRecord record = mProcessList.getLRURecordForAppLocked(thread);
+ if (record != null) return record;
// Validation: if it isn't in the LRU list, it shouldn't exist, but let's
// double-check that.
final IBinder threadBinder = thread.asBinder();
- final ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+ final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+ mProcessList.mProcessNames.getMap();
for (int i = pmap.size()-1; i >= 0; i--) {
final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
for (int j = procs.size()-1; j >= 0; j--) {
@@ -4201,17 +3162,7 @@
// If there are no longer any background processes running,
// and the app that died was not running instrumentation,
// then tell everyone we are now low on memory.
- boolean haveBg = false;
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null
- && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
- haveBg = true;
- break;
- }
- }
-
- if (!haveBg) {
+ if (!mProcessList.haveBackgroundProcessLocked()) {
boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (doReport) {
long now = SystemClock.uptimeMillis();
@@ -4222,11 +3173,12 @@
}
}
final ArrayList<ProcessMemInfo> memInfos
- = doReport ? new ArrayList<ProcessMemInfo>(mLruProcesses.size()) : null;
- EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
+ = doReport ? new ArrayList<ProcessMemInfo>(mProcessList.getLruSizeLocked())
+ : null;
+ EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mProcessList.getLruSizeLocked());
long now = SystemClock.uptimeMillis();
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord rec = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord rec = mProcessList.mLruProcesses.get(i);
if (rec == dyingProc || rec.thread == null) {
continue;
}
@@ -4283,7 +3235,7 @@
if (!fromBinderDied) {
killProcessQuiet(pid);
}
- killProcessGroup(app.uid, pid);
+ ProcessList.killProcessGroup(app.uid, pid);
app.killed = true;
}
@@ -4702,7 +3654,7 @@
return;
}
synchronized (this) {
- killPackageProcessesLocked(packageName, appId, targetUserId,
+ mProcessList.killPackageProcessesLocked(packageName, appId, targetUserId,
ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
}
}
@@ -4725,30 +3677,7 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
- final ProcessRecord app = apps.valueAt(ia);
- if (app.isPersistent()) {
- // We don't kill persistent processes.
- continue;
- }
- if (app.removed) {
- procs.add(app);
- } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- app.removed = true;
- procs.add(app);
- }
- }
- }
-
- final int N = procs.size();
- for (int i = 0; i < N; i++) {
- removeProcessLocked(procs.get(i), false, true, "kill all background");
- }
+ mProcessList.killAllBackgroundProcessesLocked();
mAllowLowerMemLevel = true;
@@ -4782,27 +3711,7 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
- final ProcessRecord app = apps.valueAt(ia);
- if (app.removed) {
- procs.add(app);
- } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
- && (maxProcState < 0 || app.setProcState > maxProcState)) {
- app.removed = true;
- procs.add(app);
- }
- }
- }
-
- final int N = procs.size();
- for (int i = 0; i < N; i++) {
- removeProcessLocked(procs.get(i), false, true, "kill all background except");
- }
+ mProcessList.killAllBackgroundProcessesExceptLocked(minTargetSdk, maxProcState);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -4919,48 +3828,7 @@
@Override
public void closeSystemDialogs(String reason) {
- enforceNotIsolatedCaller("closeSystemDialogs");
-
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- // Only allow this from foreground processes, so that background
- // applications can't abuse it to prevent system UI from being shown.
- if (uid >= FIRST_APPLICATION_UID) {
- ProcessRecord proc;
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(pid);
- }
- if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
- + " from background process " + proc);
- return;
- }
- }
- closeSystemDialogsLocked(reason);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @GuardedBy("this")
- void closeSystemDialogsLocked(String reason) {
- Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- if (reason != null) {
- intent.putExtra("reason", reason);
- }
- mWindowManager.closeSystemDialogs(reason);
-
- mStackSupervisor.closeSystemDialogsLocked();
-
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false,
- -1, SYSTEM_UID, UserHandle.USER_ALL);
+ mAtmInternal.closeSystemDialogs(reason);
}
@Override
@@ -5141,81 +4009,6 @@
null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
}
-
- @GuardedBy("this")
- private final boolean killPackageProcessesLocked(String packageName, int appId,
- int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
- boolean doit, boolean evenPersistent, String reason) {
- ArrayList<ProcessRecord> procs = new ArrayList<>();
-
- // Remove all processes this package may have touched: all with the
- // same UID (except for the system or root user), and all whose name
- // matches the package name.
- final int NP = mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
- ProcessRecord app = apps.valueAt(ia);
- if (app.isPersistent() && !evenPersistent) {
- // we don't kill persistent processes
- continue;
- }
- if (app.removed) {
- if (doit) {
- procs.add(app);
- }
- continue;
- }
-
- // Skip process if it doesn't meet our oom adj requirement.
- if (app.setAdj < minOomAdj) {
- continue;
- }
-
- // If no package is specified, we call all processes under the
- // give user id.
- if (packageName == null) {
- if (userId != UserHandle.USER_ALL && app.userId != userId) {
- continue;
- }
- if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
- continue;
- }
- // Package has been specified, we want to hit all processes
- // that match it. We need to qualify this by the processes
- // that are running under the specified app and user ID.
- } else {
- final boolean isDep = app.pkgDeps != null
- && app.pkgDeps.contains(packageName);
- if (!isDep && UserHandle.getAppId(app.uid) != appId) {
- continue;
- }
- if (userId != UserHandle.USER_ALL && app.userId != userId) {
- continue;
- }
- if (!app.pkgList.containsKey(packageName) && !isDep) {
- continue;
- }
- }
-
- // Process has passed all conditions, kill it!
- if (!doit) {
- return true;
- }
- app.removed = true;
- procs.add(app);
- }
- }
-
- int N = procs.size();
- for (int i=0; i<N; i++) {
- removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
- }
- updateOomAdjLocked();
- return N > 0;
- }
-
private void cleanupDisabledPackageComponentsLocked(
String packageName, int userId, boolean killProcess, String[] changedClasses) {
@@ -5275,15 +4068,8 @@
return;
}
- // Clean-up disabled activities.
- if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
- packageName, disabledClasses, true, false, userId) && mBooted) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
- mStackSupervisor.scheduleIdleLocked();
- }
-
- // Clean-up disabled tasks
- mActivityTaskManager.getRecentTasks().cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
+ mAtmInternal.cleanupDisabledPackageComponents(
+ packageName, disabledClasses, userId, mBooted);
// Clean-up disabled services.
mServices.bringDownDisabledPackageServicesLocked(
@@ -5343,19 +4129,12 @@
mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
}
- boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
+ boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
packageName == null ? ("stop user " + userId) : ("stop " + packageName));
- didSomething |= mActivityTaskManager.getActivityStartController().clearPendingActivityLaunches(packageName);
-
- if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
- packageName, null, doit, evenPersistent, userId)) {
- if (!doit) {
- return true;
- }
- didSomething = true;
- }
+ didSomething |=
+ mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
if (mServices.bringDownDisabledPackageServicesLocked(
packageName, null, userId, evenPersistent, true, doit)) {
@@ -5405,136 +4184,17 @@
}
}
if (mBooted) {
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
- mStackSupervisor.scheduleIdleLocked();
+ mAtmInternal.resumeTopActivities(true /* scheduleIdle */);
}
}
return didSomething;
}
- private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
- return removeProcessNameLocked(name, uid, null);
- }
-
- private final ProcessRecord removeProcessNameLocked(final String name, final int uid,
- final ProcessRecord expecting) {
- ProcessRecord old = mProcessNames.get(name, uid);
- // Only actually remove when the currently recorded value matches the
- // record that we expected; if it doesn't match then we raced with a
- // newly created process and we don't want to destroy the new one.
- if ((expecting == null) || (old == expecting)) {
- mProcessNames.remove(name, uid);
- }
- if (old != null && old.uidRecord != null) {
- old.uidRecord.numProcs--;
- if (old.uidRecord.numProcs == 0) {
- // No more processes using this uid, tell clients it is gone.
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "No more processes in " + old.uidRecord);
- enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
- EventLogTags.writeAmUidStopped(uid);
- mActiveUids.remove(uid);
- noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
- }
- old.uidRecord = null;
- }
- mIsolatedProcesses.remove(uid);
- return old;
- }
-
- private final void addProcessNameLocked(ProcessRecord proc) {
- // We shouldn't already have a process under this name, but just in case we
- // need to clean up whatever may be there now.
- ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
- if (old == proc && proc.isPersistent()) {
- // We are re-adding a persistent process. Whatevs! Just leave it there.
- Slog.w(TAG, "Re-adding persistent process " + proc);
- } else if (old != null) {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
- }
- UidRecord uidRec = mActiveUids.get(proc.uid);
- if (uidRec == null) {
- uidRec = new UidRecord(proc.uid);
- // This is the first appearance of the uid, report it now!
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Creating new process uid: " + uidRec);
- if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
- || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
- uidRec.setWhitelist = uidRec.curWhitelist = true;
- }
- uidRec.updateHasInternetPermission();
- mActiveUids.put(proc.uid, uidRec);
- EventLogTags.writeAmUidRunning(uidRec.uid);
- noteUidProcessState(uidRec.uid, uidRec.curProcState);
- }
- proc.uidRecord = uidRec;
-
- // Reset render thread tid if it was already set, so new process can set it again.
- proc.renderThreadTid = 0;
- uidRec.numProcs++;
- mProcessNames.put(proc.processName, proc.uid, proc);
- if (proc.isolated) {
- mIsolatedProcesses.put(proc.uid, proc);
- }
- }
-
- @GuardedBy("this")
- boolean removeProcessLocked(ProcessRecord app,
- boolean callerWillRestart, boolean allowRestart, String reason) {
- final String name = app.processName;
- final int uid = app.uid;
- if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
- "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
-
- ProcessRecord old = mProcessNames.get(name, uid);
- if (old != app) {
- // This process is no longer active, so nothing to do.
- Slog.w(TAG, "Ignoring remove of inactive process: " + app);
- return false;
- }
- removeProcessNameLocked(name, uid);
- mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
-
- boolean needRestart = false;
- if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
- int pid = app.pid;
- if (pid > 0) {
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
- mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
- if (app.isolated) {
- mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
- getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
- }
- }
- boolean willRestart = false;
- if (app.isPersistent() && !app.isolated) {
- if (!callerWillRestart) {
- willRestart = true;
- } else {
- needRestart = true;
- }
- }
- app.kill(reason, true);
- handleAppDiedLocked(app, willRestart, allowRestart);
- if (willRestart) {
- removeLruProcessLocked(app);
- addAppLocked(app.info, null, false, null /* ABI override */);
- }
- } else {
- mRemovedProcesses.add(app);
- }
-
- return needRestart;
- }
-
@GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
- removeProcessLocked(app, false, true, "timeout publishing content providers");
+ mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
}
private final void processStartTimedOutLocked(ProcessRecord app) {
@@ -5552,7 +4212,7 @@
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
- removeProcessNameLocked(app.processName, app.uid);
+ mProcessList.removeProcessNameLocked(app.processName, app.uid);
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
@@ -5608,9 +4268,10 @@
// It's possible that process called attachApplication before we got a chance to
// update the internal state.
if (app == null && startSeq > 0) {
- final ProcessRecord pending = mPendingStarts.get(startSeq);
+ final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
if (pending != null && pending.startUid == callingUid
- && handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(),
+ && mProcessList.handleProcessStartedLocked(pending, pid, pending
+ .isUsingWrapper(),
startSeq, true)) {
app = pending;
}
@@ -5652,7 +4313,7 @@
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
- startProcessLocked(app, "link fail", processName);
+ mProcessList.startProcessLocked(app, "link fail", processName);
return false;
}
@@ -5830,7 +4491,7 @@
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
- mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
+ mAtmInternal.preBindApplication(app.getWindowProcessController());
final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
@@ -5863,7 +4524,7 @@
profilerInfo = null;
}
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
- updateLruProcessLocked(app, false, null);
+ mProcessList.updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
@@ -5874,7 +4535,7 @@
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- startProcessLocked(app, "bind fail", processName);
+ mProcessList.startProcessLocked(app, "bind fail", processName);
return false;
}
@@ -5890,9 +4551,7 @@
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
- if (mStackSupervisor.attachApplicationLocked(app)) {
- didSomething = true;
- }
+ didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
@@ -6049,7 +4708,7 @@
for (int ip=0; ip<NP; ip++) {
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
+ procs.get(ip));
- startProcessLocked(procs.get(ip), "on-hold", null);
+ mProcessList.startProcessLocked(procs.get(ip), "on-hold", null);
}
}
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -6550,7 +5209,7 @@
for (int i = 0; i < pids.length; i++) {
ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT :
- pr.curProcState;
+ pr.getCurProcState();
if (scores != null) {
scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj;
}
@@ -6834,7 +5493,7 @@
proc = mPidsSelfLocked.get(callingPid);
}
if (proc != null &&
- !ActivityManager.isProcStateBackground(proc.curProcState)) {
+ !ActivityManager.isProcStateBackground(proc.getCurProcState())) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
return ActivityManager.APP_START_MODE_NORMAL;
@@ -6871,11 +5530,6 @@
return ptw != null ? ptw.tag : null;
}
- @VisibleForTesting
- boolean isActivityStartsLoggingEnabled() {
- return mConstants.mFlagActivityStartsLoggingEnabled;
- }
-
private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
ProviderInfo pi = null;
ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
@@ -7011,19 +5665,7 @@
@Override
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
- final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
- final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
- outInfo.availMem = getFreeMemory();
- outInfo.totalMem = getTotalMemory();
- outInfo.threshold = homeAppMem;
- outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
- outInfo.hiddenAppThreshold = cachedAppMem;
- outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
- ProcessList.SERVICE_ADJ);
- outInfo.visibleAppThreshold = mProcessList.getMemLevel(
- ProcessList.VISIBLE_APP_ADJ);
- outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
- ProcessList.FOREGROUND_APP_ADJ);
+ mProcessList.getMemoryInfo(outInfo);
}
// =========================================================
@@ -7415,7 +6057,7 @@
}
cpr.connections.add(conn);
r.conProviders.add(conn);
- startAssociationLocked(r.uid, r.processName, r.curProcState,
+ startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return conn;
}
@@ -7459,7 +6101,7 @@
return false;
}
- private void checkTime(long startTime, String where) {
+ void checkTime(long startTime, String where) {
long now = SystemClock.uptimeMillis();
if ((now-startTime) > 50) {
// If we are taking more than 50ms, log about it.
@@ -7501,6 +6143,7 @@
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
+ boolean providerRunning = false;
synchronized(this) {
long startTime = SystemClock.uptimeMillis();
@@ -7540,8 +6183,6 @@
}
}
- boolean providerRunning = false;
-
if (cpr != null && cpr.proc != null) {
providerRunning = !cpr.proc.killed;
@@ -7619,7 +6260,7 @@
// back up on the LRU list. This is good because
// content providers are often expensive to start.
checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
- updateLruProcessLocked(cpr.proc, false, null);
+ mProcessList.updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
@@ -7866,6 +6507,7 @@
}
// Wait for the provider to be published...
+ final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
@@ -7880,13 +6522,22 @@
return null;
}
try {
+ final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
if (DEBUG_MU) Slog.v(TAG_MU,
"Waiting to start provider " + cpr
- + " launchingApp=" + cpr.launchingApp);
+ + " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");
if (conn != null) {
conn.waiting = true;
}
- cpr.wait();
+ cpr.wait(wait);
+ if (cpr.provider == null) {
+ Slog.wtf(TAG, "Timeout waiting for provider "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name
+ + " providerRunning=" + providerRunning);
+ return null;
+ }
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
@@ -8246,8 +6897,8 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- mAppErrors.appNotResponding(host, null, null, false,
- "ContentProvider not responding");
+ host.appNotResponding(
+ null, null, null, null, false, "ContentProvider not responding");
}
});
}
@@ -8255,7 +6906,7 @@
public final void installSystemProviders() {
List<ProviderInfo> providers;
synchronized (this) {
- ProcessRecord app = mProcessNames.get("system", SYSTEM_UID);
+ ProcessRecord app = mProcessList.mProcessNames.get("system", SYSTEM_UID);
providers = generateApplicationProvidersLocked(app);
if (providers != null) {
for (int i=providers.size()-1; i>=0; i--) {
@@ -8317,9 +6968,10 @@
final int matchFlags = GET_PROVIDERS | MATCH_DIRECT_BOOT_UNAWARE;
synchronized (this) {
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final SparseArray<ProcessRecord> apps = mProcessList.mProcessNames.getMap().valueAt
+ (ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
@@ -8425,67 +7077,6 @@
// GLOBAL MANAGEMENT
// =========================================================
- @GuardedBy("this")
- final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
- boolean isolated, int isolatedUid) {
- String proc = customProcess != null ? customProcess : info.processName;
- final int userId = UserHandle.getUserId(info.uid);
- int uid = info.uid;
- if (isolated) {
- if (isolatedUid == 0) {
- int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
- while (true) {
- if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
- || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
- mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
- }
- uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
- mNextIsolatedProcessUid++;
- if (mIsolatedProcesses.indexOfKey(uid) < 0) {
- // No process for this uid, use it.
- break;
- }
- stepsLeft--;
- if (stepsLeft <= 0) {
- return null;
- }
- }
- } else {
- // Special case for startIsolatedProcess (internal only), where
- // the uid of the isolated process is specified by the caller.
- uid = isolatedUid;
- }
- getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
-
- // Register the isolated UID with this application so BatteryStats knows to
- // attribute resource usage to the application.
- //
- // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
- // about the process state of the isolated UID *before* it is registered with the
- // owning application.
- mBatteryStatsService.addIsolatedUid(uid, info.uid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
- StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
- }
- final ProcessRecord r = new ProcessRecord(this, info, proc, uid, getGlobalConfiguration());
- if (!mBooted && !mBooting
- && userId == UserHandle.USER_SYSTEM
- && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
- // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
- r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
- r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- r.setPersistent(true);
- r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
- }
- if (isolated && isolatedUid != 0) {
- // Special case for startIsolatedProcess (internal only) - assume the process
- // is required by the system server to prevent it being killed.
- r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
- }
- addProcessNameLocked(r);
- return r;
- }
-
private boolean uidOnBackgroundWhitelist(final int uid) {
final int appId = UserHandle.getAppId(uid);
final int[] whitelist = mBackgroundAppIdWhitelist;
@@ -8543,12 +7134,13 @@
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
- abiOverride);
+ false /* mountExtStorageFull */, abiOverride);
}
+ // TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, String abiOverride) {
+ boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -8558,8 +7150,8 @@
}
if (app == null) {
- app = newProcessRecordLocked(info, customProcess, isolated, 0);
- updateLruProcessLocked(app, false, null);
+ app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+ mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -8579,9 +7171,9 @@
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
- startProcessLocked(app, "added application",
+ mProcessList.startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
- abiOverride);
+ mountExtStorageFull, abiOverride);
}
return app;
@@ -8696,51 +7288,12 @@
@Override
public void notifyLockedProfile(@UserIdInt int userId) {
- try {
- if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
- throw new SecurityException("Only privileged app can call notifyLockedProfile");
- }
- } catch (RemoteException ex) {
- throw new SecurityException("Fail to check is caller a privileged app", ex);
- }
-
- synchronized (this) {
- final long ident = Binder.clearCallingIdentity();
- try {
- if (mUserController.shouldConfirmCredentials(userId)) {
- if (mActivityTaskManager.mKeyguardController.isKeyguardLocked()) {
- // Showing launcher to avoid user entering credential twice.
- final int currentUserId = mUserController.getCurrentUserId();
- mAtmInternal.startHomeActivity(currentUserId, "notifyLockedProfile");
- }
- mStackSupervisor.lockAllProfileTasks(userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
+ mAtmInternal.notifyLockedProfile(userId, mUserController.getCurrentUserId());
}
@Override
public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
- synchronized (this) {
- final long ident = Binder.clearCallingIdentity();
- try {
- intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
- FLAG_ACTIVITY_TASK_ON_HOME);
- ActivityOptions activityOptions = options != null
- ? new ActivityOptions(options)
- : ActivityOptions.makeBasic();
- activityOptions.setLaunchTaskId(
- mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
- mContext.startActivityAsUser(intent, activityOptions.toBundle(),
- UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
+ mAtmInternal.startConfirmDeviceCredentialIntent(intent, options);
}
@Override
@@ -9250,11 +7803,11 @@
Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
return;
}
- if (pr.hasTopUi != hasTopUi) {
+ if (pr.hasTopUi() != hasTopUi) {
if (DEBUG_OOM_ADJ) {
Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
}
- pr.hasTopUi = hasTopUi;
+ pr.setHasTopUi(hasTopUi);
changed = true;
}
}
@@ -9482,7 +8035,7 @@
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
- killPackageProcessesLocked(null, appId, userId,
+ mProcessList.killPackageProcessesLocked(null, appId, userId,
ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true,
reason != null ? reason : "kill uid");
} finally {
@@ -9626,14 +8179,20 @@
// If at least 1/3 of our time since the last idle period has been spent
// with RAM low, then we want to kill processes.
boolean doKilling = lowRamSinceLastIdle > (timeSinceLastIdle/3);
+ // If the processes' memory has increased by more than 1% of the total memory,
+ // or 10 MB, whichever is greater, then the processes' are eligible to be killed.
+ final long totalMemoryInKb = getTotalMemory() / 1000;
+ final long memoryGrowthThreshold =
+ Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord proc = mProcessList.mLruProcesses.get(i);
if (proc.notCachedSinceIdle) {
if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
if (doKilling && proc.initialIdlePss != 0
- && proc.lastPss > ((proc.initialIdlePss*3)/2)) {
+ && proc.lastPss > ((proc.initialIdlePss * 3) / 2)
+ && proc.lastPss > (proc.initialIdlePss + memoryGrowthThreshold)) {
sb = new StringBuilder(128);
sb.append("Kill");
sb.append(proc.processName);
@@ -9757,7 +8316,7 @@
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
Slog.i(TAG, "Removing system update proc: " + proc);
- removeProcessLocked(proc, true, false, "system update done");
+ mProcessList.removeProcessLocked(proc, true, false, "system update done");
}
}
@@ -9848,7 +8407,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
@@ -9947,16 +8506,17 @@
: StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN
);
- final int relaunchReason = r == null ? ActivityRecord.RELAUNCH_REASON_NONE
+ final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
: r.getWindowProcessController().computeRelaunchReason();
- final String relaunchReasonString = ActivityRecord.relaunchReasonToString(relaunchReason);
+ final String relaunchReasonString = relaunchReasonToString(relaunchReason);
if (crashInfo.crashTag == null) {
crashInfo.crashTag = relaunchReasonString;
} else {
crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
}
- addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
+ addErrorToDropBox(
+ eventType, r, processName, null, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
}
@@ -10128,7 +8688,7 @@
StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName,
callingPid);
- addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);
+ addErrorToDropBox("wtf", r, processName, null, null, null, tag, null, null, crashInfo);
return r;
}
@@ -10143,22 +8703,7 @@
}
synchronized (this) {
- final int NP = mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
- ProcessRecord p = apps.valueAt(ia);
- if (p.thread != null && p.thread.asBinder() == app) {
- return p;
- }
- }
- }
-
- Slog.w(TAG, "Can't find mystery application for " + reason
- + " from pid=" + Binder.getCallingPid()
- + " uid=" + Binder.getCallingUid() + ": " + app);
- return null;
+ return mProcessList.findAppProcessLocked(app, reason);
}
}
@@ -10227,17 +8772,18 @@
* Write a description of an error (crash, WTF, ANR) to the drop box.
* @param eventType to include in the drop box tag ("crash", "wtf", etc.)
* @param process which caused the error, null means the system server
- * @param activity which triggered the error, null if unknown
- * @param parent activity related to the error, null if unknown
+ * @param activityShortComponentName which triggered the error, null if unknown
+ * @param parentShortComponentName activity related to the error, null if unknown
+ * @param parentProcess parent process
* @param subject line related to the error, null if absent
* @param report in long form describing the error, null if absent
* @param dataFile text file to include in the report, null if none
* @param crashInfo giving an application stack trace, null if absent
*/
public void addErrorToDropBox(String eventType,
- ProcessRecord process, String processName, ActivityRecord activity,
- ActivityRecord parent, String subject,
- final String report, final File dataFile,
+ ProcessRecord process, String processName, String activityShortComponentName,
+ String parentShortComponentName, ProcessRecord parentProcess,
+ String subject, final String report, final File dataFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
@@ -10267,14 +8813,16 @@
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
.append("\n");
}
- if (activity != null) {
- sb.append("Activity: ").append(activity.shortComponentName).append("\n");
+ if (activityShortComponentName != null) {
+ sb.append("Activity: ").append(activityShortComponentName).append("\n");
}
- if (parent != null && parent.app != null && parent.app.getPid() != process.pid) {
- sb.append("Parent-Process: ").append(parent.app.mName).append("\n");
- }
- if (parent != null && parent != activity) {
- sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
+ if (parentShortComponentName != null) {
+ if (parentProcess != null && parentProcess.pid != process.pid) {
+ sb.append("Parent-Process: ").append(parentProcess.processName).append("\n");
+ }
+ if (!parentShortComponentName.equals(activityShortComponentName)) {
+ sb.append("Parent-Activity: ").append(parentShortComponentName).append("\n");
+ }
}
if (subject != null) {
sb.append("Subject: ").append(subject).append("\n");
@@ -10371,8 +8919,8 @@
synchronized (this) {
// iterate across all processes
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!allUsers && app.userId != userId) {
continue;
}
@@ -10405,44 +8953,6 @@
return errList;
}
- static int procStateToImportance(int procState, int memAdj,
- ActivityManager.RunningAppProcessInfo currApp,
- int clientTargetSdk) {
- int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
- procState, clientTargetSdk);
- if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- currApp.lru = memAdj;
- } else {
- currApp.lru = 0;
- }
- return imp;
- }
-
- @GuardedBy("this")
- private void fillInProcMemInfoLocked(ProcessRecord app,
- ActivityManager.RunningAppProcessInfo outInfo,
- int clientTargetSdk) {
- outInfo.pid = app.pid;
- outInfo.uid = app.info.uid;
- if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
- outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
- }
- if (app.isPersistent()) {
- outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
- }
- if (app.hasActivities()) {
- outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
- }
- outInfo.lastTrimLevel = app.trimMemoryLevel;
- int adj = app.curAdj;
- int procState = app.curProcState;
- outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
- outInfo.importanceReasonCode = app.adjTypeCode;
- outInfo.processState = app.curProcState;
- outInfo.isFocused = (app == getTopAppLocked());
- outInfo.lastActivityTime = app.lastActivityTime;
- }
-
@Override
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
@@ -10450,8 +8960,6 @@
final int callingUid = Binder.getCallingUid();
final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
- // Lazy instantiation of list
- List<ActivityManager.RunningAppProcessInfo> runList = null;
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
callingUid) == PackageManager.PERMISSION_GRANTED;
final int userId = UserHandle.getUserId(callingUid);
@@ -10460,44 +8968,9 @@
synchronized (this) {
// Iterate across all processes
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
- if ((!allUsers && app.userId != userId)
- || (!allUids && app.uid != callingUid)) {
- continue;
- }
- if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
- // Generate process state info for running application
- ActivityManager.RunningAppProcessInfo currApp =
- new ActivityManager.RunningAppProcessInfo(app.processName,
- app.pid, app.getPackageList());
- fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
- if (app.adjSource instanceof ProcessRecord) {
- currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
- currApp.importanceReasonImportance =
- ActivityManager.RunningAppProcessInfo.procStateToImportance(
- app.adjSourceProcState);
- } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
- ActivityServiceConnectionsHolder r =
- (ActivityServiceConnectionsHolder) app.adjSource;
- final int pid = r.getActivityPid();
- if (pid != -1) {
- currApp.importanceReasonPid = pid;
- }
- }
- if (app.adjTarget instanceof ComponentName) {
- currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
- }
- //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
- // + " lru=" + currApp.lru);
- if (runList == null) {
- runList = new ArrayList<>();
- }
- runList.add(currApp);
- }
- }
+ return mProcessList.getRunningAppProcessesLocked(allUsers, userId, allUids,
+ callingUid, clientTargetSdk);
}
- return runList;
}
@Override
@@ -10544,7 +9017,7 @@
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
- fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
+ mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
}
}
}
@@ -10629,24 +9102,26 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- if (mActivityTaskManager.getRecentTasks() != null) {
- mActivityTaskManager.getRecentTasks().dump(pw, dumpAll, dumpPackage);
- }
+ mAtmInternal.dump(
+ DUMP_RECENTS_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpLastANRLocked(pw);
+ mAtmInternal.dump(
+ DUMP_LASTANR_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpActivityStarterLocked(pw, dumpPackage);
+ mAtmInternal.dump(
+ DUMP_STARTER_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpActivityContainersLocked(pw);
+ mAtmInternal.dump(
+ DUMP_CONTAINERS_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
// Activities section is dumped as part of the Critical priority dump. Exclude the
// section if priority is Normal.
if (!dumpNormalPriority) {
@@ -10654,7 +9129,8 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ mAtmInternal.dump(
+ DUMP_ACTIVITIES_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
if (mAssociations.size() > 0) {
pw.println();
@@ -10743,9 +9219,7 @@
if ("activities".equals(cmd) || "a".equals(cmd)) {
// output proto is ActivityManagerServiceDumpActivitiesProto
- synchronized (this) {
- writeActivitiesToProtoLocked(proto);
- }
+ mAtmInternal.writeActivitiesToProto(proto);
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
// output proto is ActivityManagerServiceDumpBroadcastsProto
synchronized (this) {
@@ -10784,7 +9258,7 @@
// default option, dump everything, output is ActivityManagerServiceProto
synchronized (this) {
long activityToken = proto.start(ActivityManagerServiceProto.ACTIVITIES);
- writeActivitiesToProtoLocked(proto);
+ mAtmInternal.writeActivitiesToProto(proto);
proto.end(activityToken);
long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
@@ -10811,32 +9285,12 @@
if (opti < args.length) {
String cmd = args[opti];
opti++;
- if ("activities".equals(cmd) || "a".equals(cmd)) {
- synchronized (this) {
- dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
- }
- } else if ("lastanr".equals(cmd)) {
- synchronized (this) {
- dumpLastANRLocked(pw);
- }
- } else if ("lastanr-traces".equals(cmd)) {
- synchronized (this) {
- dumpLastANRTracesLocked(pw);
- }
- } else if ("starter".equals(cmd)) {
- synchronized (this) {
- dumpActivityStarterLocked(pw, dumpPackage);
- }
- } else if ("containers".equals(cmd)) {
- synchronized (this) {
- dumpActivityContainersLocked(pw);
- }
- } else if ("recents".equals(cmd) || "r".equals(cmd)) {
- synchronized (this) {
- if (mActivityTaskManager.getRecentTasks() != null) {
- mActivityTaskManager.getRecentTasks().dump(pw, true /* dumpAll */, dumpPackage);
- }
- }
+ if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)
+ || DUMP_LASTANR_CMD.equals(cmd) || DUMP_LASTANR_TRACES_CMD.equals(cmd)
+ || DUMP_STARTER_CMD.equals(cmd) || DUMP_CONTAINERS_CMD.equals(cmd)
+ || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+ mAtmInternal.dump(
+ cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
dumpBinderProxies(pw);
@@ -10972,8 +9426,8 @@
LockGuard.dump(fd, pw, args);
} else {
// Dumping a single activity?
- if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacksOnly,
- dumpFocusedStackOnly)) {
+ if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
+ dumpVisibleStacksOnly, dumpFocusedStackOnly)) {
ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
int res = shell.exec(this, null, fd, null, args, null,
new ResultReceiver(null));
@@ -11010,96 +9464,6 @@
Binder.restoreCallingIdentity(origId);
}
- private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
- // The output proto of "activity --proto activities" is ActivityManagerServiceDumpActivitiesProto
- mStackSupervisor.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
- }
-
- private void dumpLastANRLocked(PrintWriter pw) {
- pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
- if (mLastANRState == null) {
- pw.println(" <no ANR has occurred since boot>");
- } else {
- pw.println(mLastANRState);
- }
- }
-
- private void dumpLastANRTracesLocked(PrintWriter pw) {
- pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
-
- final File[] files = new File(ANR_TRACE_DIR).listFiles();
- if (ArrayUtils.isEmpty(files)) {
- pw.println(" <no ANR has occurred since boot>");
- return;
- }
- // Find the latest file.
- File latest = null;
- for (File f : files) {
- if ((latest == null) || (latest.lastModified() < f.lastModified())) {
- latest = f;
- }
- }
- pw.print("File: ");
- pw.print(latest.getName());
- pw.println();
- try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
- String line;
- while ((line = in.readLine()) != null) {
- pw.println(line);
- }
- } catch (IOException e) {
- pw.print("Unable to read: ");
- pw.print(e);
- pw.println();
- }
- }
-
- private void dumpActivityContainersLocked(PrintWriter pw) {
- pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
- mStackSupervisor.dumpChildrenNames(pw, " ");
- pw.println(" ");
- }
-
- private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
- pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
- mActivityTaskManager.getActivityStartController().dump(pw, "", dumpPackage);
- }
-
- void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
- dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
- "ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
- }
-
- void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
- pw.println(header);
-
- boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
- dumpPackage);
- boolean needSep = printedAnything;
-
- boolean printed = ActivityStackSupervisor.printThisActivity(pw,
- mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep,
- " ResumedActivity: ");
- if (printed) {
- printedAnything = true;
- needSep = false;
- }
-
- if (dumpPackage == null) {
- if (needSep) {
- pw.println();
- }
- printedAnything = true;
- mStackSupervisor.dump(pw, " ");
- }
-
- if (!printedAnything) {
- pw.println(" (nothing)");
- }
- }
-
void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)");
@@ -11270,6 +9634,7 @@
"Counts of Binder Proxies held by SYSTEM");
}
+ // TODO: Move to ProcessList?
@GuardedBy("this")
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
@@ -11279,9 +9644,9 @@
pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
if (dumpAll) {
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord r = procs.valueAt(ia);
@@ -11303,10 +9668,10 @@
}
}
- if (mIsolatedProcesses.size() > 0) {
+ if (mProcessList.mIsolatedProcesses.size() > 0) {
boolean printed = false;
- for (int i=0; i<mIsolatedProcesses.size(); i++) {
- ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+ ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
@@ -11359,17 +9724,13 @@
}
}
- if (mLruProcesses.size() > 0) {
+ if (mProcessList.getLruSizeLocked() > 0) {
if (needSep) {
pw.println();
}
- pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
- pw.print(" total, non-act at ");
- pw.print(mLruProcesses.size()-mLruProcessActivityStart);
- pw.print(", non-svc at ");
- pw.print(mLruProcesses.size()-mLruProcessServiceStart);
- pw.println("):");
- dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", false, dumpPackage);
+ mProcessList.dumpLruListHeaderLocked(pw);
+ dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", false,
+ dumpPackage);
needSep = true;
}
@@ -11423,11 +9784,11 @@
"Starting Norm", "Restarting PERS", dumpPackage);
}
- if (mRemovedProcesses.size() > 0) {
+ if (mProcessList.mRemovedProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" Processes that are being removed:");
- dumpProcessList(pw, this, mRemovedProcesses, " ",
+ dumpProcessList(pw, this, mProcessList.mRemovedProcesses, " ",
"Removed Norm", "Removed PERS", dumpPackage);
}
@@ -11448,71 +9809,20 @@
needSep = false;
mUserController.dump(pw, dumpAll);
}
- if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
- || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
- }
- if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
- || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
- }
- if (dumpAll && (mActivityTaskManager.mPreviousProcess == null || dumpPackage == null
- || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
- StringBuilder sb = new StringBuilder(128);
- sb.append(" mPreviousProcessVisibleTime: ");
- TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
- pw.println(sb);
- }
- if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
- || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
- }
- if (dumpAll && mPendingStarts.size() > 0) {
+
+ needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
+ mTestPssMode, mWakefulness);
+
+ if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" mPendingStarts: ");
- for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
- pw.println(" " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
+ for (int i = 0, len = mProcessList.mPendingStarts.size(); i < len; ++i ) {
+ pw.println(" " + mProcessList.mPendingStarts.keyAt(i) + ": "
+ + mProcessList.mPendingStarts.valueAt(i));
}
}
- if (dumpPackage == null) {
- pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
- mStackSupervisor.dumpDisplayConfigs(pw, " ");
- }
if (dumpAll) {
- if (dumpPackage == null) {
- pw.println(" mConfigWillChange: "
- + mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
- }
- if (mActivityTaskManager.mCompatModePackages.getPackages().size() > 0) {
- boolean printed = false;
- for (Map.Entry<String, Integer> entry
- : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
- String pkg = entry.getKey();
- int mode = entry.getValue();
- if (dumpPackage != null && !dumpPackage.equals(pkg)) {
- continue;
- }
- if (!printed) {
- pw.println(" mScreenCompatPackages:");
- printed = true;
- }
- pw.print(" "); pw.print(pkg); pw.print(": ");
- pw.print(mode); pw.println();
- }
- }
final int NI = mUidObservers.getRegisteredCallbackCount();
boolean printed = false;
for (int i=0; i<NI; i++) {
@@ -11569,11 +9879,6 @@
}
}
}
- if (dumpPackage == null) {
- pw.println(" mWakefulness="
- + PowerManagerInternal.wakefulnessToString(mWakefulness));
- mActivityTaskManager.dumpSleepStates(pw, mTestPssMode);
- }
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
@@ -11587,9 +9892,6 @@
+ " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
}
}
- if (mActivityTaskManager.mCurAppTimeTracker != null) {
- mActivityTaskManager.mCurAppTimeTracker.dumpWithHeader(pw, " ", true);
- }
if (mMemWatchProcesses.getMap().size() > 0) {
pw.println(" Mem watch processes:");
final ArrayMap<String, SparseArray<Pair<Long, String>>> procs
@@ -11654,40 +9956,10 @@
pw.println(" mNativeDebuggingApp=" + mNativeDebuggingApp);
}
}
- if (mActivityTaskManager.mAllowAppSwitchUids.size() > 0) {
- boolean printed = false;
- for (int i = 0; i < mActivityTaskManager.mAllowAppSwitchUids.size(); i++) {
- ArrayMap<String, Integer> types = mActivityTaskManager.mAllowAppSwitchUids.valueAt(i);
- for (int j = 0; j < types.size(); j++) {
- if (dumpPackage == null ||
- UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
- if (needSep) {
- pw.println();
- needSep = false;
- }
- if (!printed) {
- pw.println(" mAllowAppSwitchUids:");
- printed = true;
- }
- pw.print(" User ");
- pw.print(mActivityTaskManager.mAllowAppSwitchUids.keyAt(i));
- pw.print(": Type ");
- pw.print(types.keyAt(j));
- pw.print(" = ");
- UserHandle.formatUid(pw, types.valueAt(j).intValue());
- pw.println();
- }
- }
- }
- }
if (dumpPackage == null) {
if (mAlwaysFinishActivities) {
pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities);
}
- if (mActivityTaskManager.mController != null) {
- pw.println(" mController=" + mActivityTaskManager.mController
- + " mControllerIsAMonkey=" + mActivityTaskManager.mControllerIsAMonkey);
- }
if (dumpAll) {
pw.println(" Total persistent processes: " + numPers);
pw.println(" mProcessesReady=" + mProcessesReady
@@ -11700,11 +9972,9 @@
pw.print(" mLastPowerCheckUptime=");
TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
pw.println("");
- pw.println(" mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
- pw.println(" mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
- pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
+ pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs
- + " (" + mLruProcesses.size() + " total)"
+ + " (" + mProcessList.getLruSizeLocked() + " total)"
+ " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+ " mNumServiceProcs=" + mNumServiceProcs
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
@@ -11750,9 +10020,9 @@
void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia = 0; ia<NA; ia++) {
ProcessRecord r = procs.valueAt(ia);
@@ -11766,8 +10036,8 @@
}
}
- for (int i=0; i<mIsolatedProcesses.size(); i++) {
- ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+ ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
@@ -11800,14 +10070,17 @@
uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
}
- if (mLruProcesses.size() > 0) {
+ if (mProcessList.getLruSizeLocked() > 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
- int total = mLruProcesses.size();
+ int total = mProcessList.getLruSizeLocked();
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.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
+ total - mProcessList.mLruProcessActivityStart);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
+ total - mProcessList.mLruProcessServiceStart);
+ writeProcessOomListToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
+ mProcessList.mLruProcesses,false, dumpPackage);
proto.end(lruToken);
}
@@ -11818,7 +10091,8 @@
if (!r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
+ r.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
}
}
}
@@ -11832,7 +10106,8 @@
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
- it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
+ it.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
}
}
}
@@ -11842,11 +10117,12 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
+ r.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
}
- for (int i=0; i<mRemovedProcesses.size(); i++) {
- ProcessRecord r = mRemovedProcesses.get(i);
+ for (int i = 0; i < mProcessList.mRemovedProcesses.size(); i++) {
+ ProcessRecord r = mProcessList.mRemovedProcesses.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
@@ -11863,39 +10139,10 @@
writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
+ mAtmInternal.writeProcessesToProto(proto, dumpPackage);
if (dumpPackage == null) {
mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
- getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
- proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
- }
-
- if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
- || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
- ((ProcessRecord) mActivityTaskManager.mHomeProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
- }
-
- if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
- || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
- ((ProcessRecord) mActivityTaskManager.mPreviousProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
- proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
- }
-
- if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
- || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
- ((ProcessRecord) mActivityTaskManager.mHeavyWeightProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
- }
-
- for (Map.Entry<String, Integer> entry
- : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
- String pkg = entry.getKey();
- int mode = entry.getValue();
- if (dumpPackage == null || dumpPackage.equals(pkg)) {
- long compatToken = proto.start(ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES);
- proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
- proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE, mode);
- proto.end(compatToken);
- }
}
final int NI = mUidObservers.getRegisteredCallbackCount();
@@ -11928,8 +10175,6 @@
PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
proto.end(sleepToken);
-
- mActivityTaskManager.writeSleepStateToProto(proto);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -11945,11 +10190,6 @@
}
}
- if (mActivityTaskManager.mCurAppTimeTracker != null) {
- mActivityTaskManager.mCurAppTimeTracker.writeToProto(
- proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
- }
-
if (mMemWatchProcesses.getMap().size() > 0) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
@@ -12006,12 +10246,6 @@
if (dumpPackage == null) {
proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
- if (mActivityTaskManager.mController != null) {
- final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
- proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mActivityTaskManager.mController.toString());
- proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mActivityTaskManager.mControllerIsAMonkey);
- proto.end(token);
- }
proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
proto.write(ActivityManagerServiceDumpProcessesProto.PROCESSES_READY, mProcessesReady);
proto.write(ActivityManagerServiceDumpProcessesProto.SYSTEM_READY, mSystemReady);
@@ -12021,10 +10255,8 @@
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.LRU_SEQ, mProcessList.mLruSeq);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
@@ -12105,7 +10337,7 @@
int opti, boolean dumpAll) {
boolean needSep = false;
- if (mLruProcesses.size() > 0) {
+ if (mProcessList.getLruSizeLocked() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" OOM levels:");
@@ -12125,13 +10357,16 @@
printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
if (needSep) pw.println();
- pw.print(" Process OOM control ("); pw.print(mLruProcesses.size());
+ pw.print(" Process OOM control ("); pw.print(mProcessList.getLruSizeLocked());
pw.print(" total, non-act at ");
- pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+ pw.print(mProcessList.getLruSizeLocked()
+ - mProcessList.mLruProcessActivityStart);
pw.print(", non-svc at ");
- pw.print(mLruProcesses.size()-mLruProcessServiceStart);
+ pw.print(mProcessList.getLruSizeLocked()
+ - mProcessList.mLruProcessServiceStart);
pw.println("):");
- dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", true, null);
+ dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", true,
+ null);
needSep = true;
}
@@ -12249,95 +10484,6 @@
}
}
- /**
- * There are three things that cmd can be:
- * - a flattened component name that matches an existing activity
- * - the cmd arg isn't the flattened component name of an existing activity:
- * dump all activity whose component contains the cmd as a substring
- * - A hex number of the ActivityRecord object instance.
- *
- * @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
- * @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
- */
- protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
- int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
- ArrayList<ActivityRecord> activities;
-
- synchronized (this) {
- activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
- dumpFocusedStackOnly);
- }
-
- if (activities.size() <= 0) {
- return false;
- }
-
- String[] newArgs = new String[args.length - opti];
- System.arraycopy(args, opti, newArgs, 0, args.length - opti);
-
- TaskRecord lastTask = null;
- boolean needSep = false;
- for (int i=activities.size()-1; i>=0; i--) {
- ActivityRecord r = activities.get(i);
- if (needSep) {
- pw.println();
- }
- needSep = true;
- synchronized (this) {
- final TaskRecord task = r.getTask();
- if (lastTask != task) {
- lastTask = task;
- pw.print("TASK "); pw.print(lastTask.affinity);
- pw.print(" id="); pw.print(lastTask.taskId);
- pw.print(" userId="); pw.println(lastTask.userId);
- if (dumpAll) {
- lastTask.dump(pw, " ");
- }
- }
- }
- dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
- }
- return true;
- }
-
- /**
- * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
- * there is a thread associated with the activity.
- */
- private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
- final ActivityRecord r, String[] args, boolean dumpAll) {
- String innerPrefix = prefix + " ";
- synchronized (this) {
- pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
- pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
- pw.print(" pid=");
- if (r.hasProcess()) pw.println(r.app.getPid());
- else pw.println("(not running)");
- if (dumpAll) {
- r.dump(pw, innerPrefix);
- }
- }
- if (r.attachedToProcess()) {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.getThread().dumpActivity(tp.getWriteFd(),
- r.appToken, innerPrefix, args);
- tp.go(fd);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(innerPrefix + "Failure while dumping the activity: " + e);
- } catch (RemoteException e) {
- pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
- }
- }
- }
-
void writeBroadcastsToProtoLocked(ProtoOutputStream proto) {
if (mRegisteredReceivers.size() > 0) {
Iterator it = mRegisteredReceivers.values().iterator();
@@ -12662,12 +10808,13 @@
if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
}
- if (r.foregroundActivities) {
+ if (r.hasForegroundActivities()) {
proto.write(ProcessOomProto.ACTIVITIES, true);
} else if (r.hasForegroundServices()) {
proto.write(ProcessOomProto.SERVICES, true);
}
- proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
+ proto.write(ProcessOomProto.STATE,
+ ProcessList.makeProcStateProtoEnum(r.getCurProcState()));
proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.trimMemoryLevel);
r.writeToProto(proto, ProcessOomProto.PROC);
proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
@@ -12688,12 +10835,12 @@
if (inclDetails) {
long detailToken = proto.start(ProcessOomProto.DETAIL);
proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
- proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.curRawAdj);
+ proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.getCurRawAdj());
proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
proto.write(ProcessOomProto.Detail.CURRENT_STATE,
- ProcessList.makeProcStateProtoEnum(r.curProcState));
+ ProcessList.makeProcStateProtoEnum(r.getCurProcState()));
proto.write(ProcessOomProto.Detail.SET_STATE,
ProcessList.makeProcStateProtoEnum(r.setProcState));
proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
@@ -12759,14 +10906,14 @@
break;
}
char foreground;
- if (r.foregroundActivities) {
+ if (r.hasForegroundActivities()) {
foreground = 'A';
} else if (r.hasForegroundServices()) {
foreground = 'S';
} else {
foreground = ' ';
}
- String procState = ProcessList.makeProcStateString(r.curProcState);
+ String procState = ProcessList.makeProcStateString(r.getCurProcState());
pw.print(prefix);
pw.print(r.isPersistent() ? persistentLabel : normalLabel);
pw.print(" #");
@@ -12814,13 +10961,14 @@
pw.print(prefix);
pw.print(" ");
pw.print("oom: max="); pw.print(r.maxAdj);
- pw.print(" curRaw="); pw.print(r.curRawAdj);
+ pw.print(" curRaw="); pw.print(r.getCurRawAdj());
pw.print(" setRaw="); pw.print(r.setRawAdj);
pw.print(" cur="); pw.print(r.curAdj);
pw.print(" set="); pw.println(r.setAdj);
pw.print(prefix);
pw.print(" ");
- pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
+ pw.print("state: cur="); pw.print(
+ ProcessList.makeProcStateString(r.getCurProcState()));
pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
@@ -12853,35 +11001,9 @@
ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
String[] args) {
- ArrayList<ProcessRecord> procs;
synchronized (this) {
- if (args != null && args.length > start
- && args[start].charAt(0) != '-') {
- procs = new ArrayList<ProcessRecord>();
- int pid = -1;
- try {
- pid = Integer.parseInt(args[start]);
- } catch (NumberFormatException e) {
- }
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
- if (proc.pid > 0 && proc.pid == pid) {
- procs.add(proc);
- } else if (allPkgs && proc.pkgList != null
- && proc.pkgList.containsKey(args[start])) {
- procs.add(proc);
- } else if (proc.processName.equals(args[start])) {
- procs.add(proc);
- }
- }
- if (procs.size() <= 0) {
- return null;
- }
- } else {
- procs = new ArrayList<ProcessRecord>(mLruProcesses);
- }
+ return mProcessList.collectProcessesLocked(start, allPkgs, args);
}
- return procs;
}
final void dumpGraphicsHardwareUsage(FileDescriptor fd,
@@ -14475,13 +12597,13 @@
mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
false, null).dumpLocked();
catPw.println();
- dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
+ mAtmInternal.dump(DUMP_ACTIVITIES_CMD, null, catPw, emptyArgs, 0, false, false, null);
catPw.flush();
}
dropBuilder.append(catSw.toString());
StatsLog.write(StatsLog.LOW_MEM_REPORTED);
addErrorToDropBox("lowmem", null, "system_server", null,
- null, tag.toString(), dropBuilder.toString(), null, null);
+ null, null, tag.toString(), dropBuilder.toString(), null, null);
//Slog.i(TAG, "Sent to dropbox:");
//Slog.i(TAG, dropBuilder.toString());
synchronized (ActivityManagerService.this) {
@@ -14576,7 +12698,7 @@
* app that was passed in must remain on the process lists.
*/
@GuardedBy("this")
- private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
+ final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
if (index >= 0) {
removeLruProcessLocked(app);
@@ -14610,11 +12732,11 @@
app.waitingToKill = null;
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
- app.foregroundActivities = false;
+ app.setHasForegroundActivities(false);
app.hasShownUi = false;
app.treatLikeActivity = false;
app.hasAboveClient = false;
- app.hasClientActivities = false;
+ app.setHasClientActivities(false);
mServices.killServicesLocked(app, allowRestart);
@@ -14716,7 +12838,7 @@
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
if (!replacingPid) {
- removeProcessNameLocked(app.processName, app.uid, app);
+ mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
}
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
} else if (!app.removed) {
@@ -14740,9 +12862,9 @@
if (index < 0) {
ProcessList.remove(app.pid);
}
- addProcessNameLocked(app);
+ mProcessList.addProcessNameLocked(app);
app.pendingStart = false;
- startProcessLocked(app, "restart", app.processName);
+ mProcessList.startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
@@ -15479,15 +13601,7 @@
}
private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
- try {
- r.thread.dispatchPackageBroadcast(cmd, packages);
- } catch (RemoteException ex) {
- }
- }
- }
+ mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
}
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
@@ -15896,8 +14010,10 @@
}
} else {
if (killProcess) {
- killPackageProcessesLocked(ssp, UserHandle.getAppId(
- intent.getIntExtra(Intent.EXTRA_UID, -1)),
+ final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
+ -1);
+ mProcessList.killPackageProcessesLocked(ssp,
+ UserHandle.getAppId(extraUid),
userId, ProcessList.INVALID_ADJ,
false, true, true, false, "change " + ssp);
}
@@ -15938,7 +14054,7 @@
+ " ssp=" + ssp + " data=" + data);
return ActivityManager.BROADCAST_SUCCESS;
}
- mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ mAtmInternal.onPackageReplaced(aInfo);
mServices.updateServiceApplicationInfoLocked(aInfo);
sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
new String[] {ssp}, userId);
@@ -16583,11 +14699,13 @@
activeInstr.mResultClass = className;
boolean disableHiddenApiChecks = ai.usesNonSdkApi()
- || (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+ || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
if (disableHiddenApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
"disable hidden API checks");
}
+ final boolean mountExtStorageFull = isCallerShell()
+ && (flags & INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL) != 0;
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
@@ -16600,7 +14718,7 @@
}
ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- abiOverride);
+ mountExtStorageFull, abiOverride);
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mRunningProcesses.add(app);
@@ -16613,6 +14731,11 @@
return true;
}
+ private boolean isCallerShell() {
+ final int callingUid = Binder.getCallingUid();
+ return callingUid == SHELL_UID || callingUid == ROOT_UID;
+ }
+
/**
* Report errors that occur while attempting to start Instrumentation. Always writes the
* error to the logs, but if somebody is watching, send the report there too. This enables
@@ -16886,7 +15009,7 @@
}
}
- private void noteUidProcessState(final int uid, final int state) {
+ void noteUidProcessState(final int uid, final int state) {
mBatteryStatsService.noteUidProcessState(uid, state);
mAppOpsService.updateUidProcState(uid, state);
if (mTrackingAssociations) {
@@ -16918,6 +15041,7 @@
private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
+ /** These methods are called inline during computeOomAdjLocked(), on the same thread */
private final class ComputeOomAdjWindowCallback
implements WindowProcessController.ComputeOomAdjCallback {
@@ -17056,8 +15180,9 @@
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
- app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ;
+ app.setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
+ app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
app.completedAdjSeq = app.adjSeq;
return false;
}
@@ -17073,7 +15198,7 @@
final int logUid = mCurOomAdjUid;
int prevAppAdj = app.curAdj;
- int prevProcState = app.curProcState;
+ int prevProcState = app.getCurProcState();
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
// The max adjustment doesn't allow this app to be anything
@@ -17083,10 +15208,10 @@
}
app.adjType = "fixed";
app.adjSeq = mAdjSeq;
- app.curRawAdj = app.maxAdj;
- app.foregroundActivities = false;
+ app.setCurRawAdj(app.maxAdj);
+ app.setHasForegroundActivities(false);
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
- app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
+ app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
// facilitate this, here we need to determine whether or not it
@@ -17096,7 +15221,7 @@
app.systemNoUi = false;
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
app.adjType = "pers-top-activity";
- } else if (app.hasTopUi) {
+ } else if (app.hasTopUi()) {
// sched group/proc state adjustment is below
app.systemNoUi = false;
app.adjType = "pers-top-ui";
@@ -17106,11 +15231,11 @@
if (!app.systemNoUi) {
if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// screen on, promote UI
- app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+ app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
} else {
// screen off, restrict UI scheduling
- app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
}
}
@@ -17246,7 +15371,7 @@
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
}
- } else if (app.hasOverlayUi) {
+ } else if (app.hasOverlayUi()) {
// The process is display an overlay UI.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
@@ -17360,7 +15485,7 @@
// there are applications dependent on our services or providers, but
// this gives us a baseline and makes sure we don't get into an
// infinite recursion.
- app.curRawAdj = adj;
+ app.setCurRawAdj(adj);
app.hasStartedServices = false;
app.adjSeq = mAdjSeq;
@@ -17475,8 +15600,8 @@
continue;
}
}
- int clientAdj = client.curRawAdj;
- int clientProcState = client.curProcState;
+ int clientAdj = client.getCurRawAdj();
+ int clientProcState = client.getCurProcState();
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
@@ -17637,8 +15762,8 @@
}
}
if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- && (cr.flags&Context.BIND_SHOWING_UI) != 0) {
- app.pendingUiClean = true;
+ && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
+ app.setPendingUiClean(true);
}
if (adjType != null) {
app.adjType = adjType;
@@ -17717,8 +15842,8 @@
continue;
}
}
- int clientAdj = client.curRawAdj;
- int clientProcState = client.curProcState;
+ int clientAdj = client.getCurRawAdj();
+ int clientProcState = client.getCurProcState();
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
@@ -17879,7 +16004,7 @@
}
if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- if (app.hasClientActivities) {
+ if (app.hasClientActivities()) {
// This is a cached process, but with client activities. Mark it so.
procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
app.adjType = "cch-client-act";
@@ -17919,7 +16044,7 @@
}
}
- app.curRawAdj = adj;
+ app.setCurRawAdj(adj);
//Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
// " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
@@ -17946,12 +16071,12 @@
// keep it out of the cached vaues.
app.curAdj = app.modifyRawOomAdj(adj);
app.setCurrentSchedulingGroup(schedGroup);
- app.curProcState = procState;
- app.foregroundActivities = foregroundActivities;
+ app.setCurProcState(procState);
+ app.setHasForegroundActivities(foregroundActivities);
app.completedAdjSeq = mAdjSeq;
// if curAdj or curProcState improved, then this process was promoted
- return app.curAdj < prevAppAdj || app.curProcState < prevProcState;
+ return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
}
/**
@@ -18098,12 +16223,12 @@
for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
}
- mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
+ mPendingPssProcesses.ensureCapacity(mProcessList.getLruSizeLocked());
mPendingPssProcesses.clear();
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ for (int i = mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (app.thread == null
- || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+ || app.getCurProcState() == ActivityManager.PROCESS_STATE_NONEXISTENT) {
continue;
}
if (memLowered || (always && now >
@@ -18112,7 +16237,7 @@
app.pssProcState = app.setProcState;
app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
: ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
- app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+ app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
mPendingPssProcesses.add(app);
}
@@ -18162,8 +16287,7 @@
processingBroadcasts = true;
}
}
- return !processingBroadcasts
- && (mAtmInternal.isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
+ return !processingBroadcasts && mAtmInternal.canGcNow();
}
/**
@@ -18178,7 +16302,7 @@
if (canGcNowLocked()) {
while (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.remove(0);
- if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
+ if (proc.getCurRawAdj() > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
if ((proc.lastRequestedGc+mConstants.GC_MIN_INTERVAL)
<= SystemClock.uptimeMillis()) {
// To avoid spamming the system, we will GC processes one
@@ -18278,10 +16402,10 @@
final long curUptime = SystemClock.uptimeMillis();
final long uptimeSince = curUptime - mLastPowerCheckUptime;
mLastPowerCheckUptime = curUptime;
- int i = mLruProcesses.size();
+ int i = mProcessList.mLruProcesses.size();
while (i > 0) {
i--;
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
if (app.lastCpuTime <= 0) {
continue;
@@ -18305,7 +16429,7 @@
if (doCpuKills && uptimeSince > 0) {
// What is the limit for this process?
int cpuLimit;
- long checkDur = curUptime - app.whenUnimportant;
+ long checkDur = curUptime - app.getWhenUnimportant();
if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;
} else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL*2)
@@ -18343,8 +16467,8 @@
long nowElapsed) {
boolean success = true;
- if (app.curRawAdj != app.setRawAdj) {
- app.setRawAdj = app.curRawAdj;
+ if (app.getCurRawAdj() != app.setRawAdj) {
+ app.setRawAdj = app.getCurRawAdj();
}
int changes = 0;
@@ -18467,12 +16591,12 @@
}
}
}
- if (app.repForegroundActivities != app.foregroundActivities) {
- app.repForegroundActivities = app.foregroundActivities;
+ if (app.repForegroundActivities != app.hasForegroundActivities()) {
+ app.repForegroundActivities = app.hasForegroundActivities();
changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
}
- if (app.getReportedProcState() != app.curProcState) {
- app.setReportedProcState(app.curProcState);
+ if (app.getReportedProcState() != app.getCurProcState()) {
+ app.setReportedProcState(app.getCurProcState());
if (app.thread != null) {
try {
if (false) {
@@ -18486,7 +16610,7 @@
}
}
if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
- || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
+ || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
// Experimental code to more aggressively collect pss while
// running test... the problem is that this tends to collect
@@ -18496,46 +16620,45 @@
long startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(app.pid, mTmpLong, null);
long endTime = SystemClock.currentThreadTimeMillis();
- recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1],
+ recordPssSampleLocked(app, app.getCurProcState(), pss, mTmpLong[0], mTmpLong[1],
mTmpLong[2], ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
mPendingPssProcesses.remove(app);
Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
- + " to " + app.curProcState + ": "
+ + " to " + app.getCurProcState() + ": "
+ (SystemClock.uptimeMillis()-start) + "ms");
}
app.lastStateTime = now;
- app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+ app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
+ ProcessList.makeProcStateString(app.setProcState) + " to "
- + ProcessList.makeProcStateString(app.curProcState) + " next pss in "
+ + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+ (app.nextPssTime-now) + ": " + app);
} else {
if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
&& now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
mTestPssMode)))) {
if (requestPssLocked(app, app.setProcState)) {
- app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+ app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
}
} else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
"Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
}
- if (app.setProcState != app.curProcState) {
+ if (app.setProcState != app.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
String msg = "Proc state change of " + app.processName
- + " to " + ProcessList.makeProcStateString(app.curProcState)
- + " (" + app.curProcState + ")" + ": " + app.adjType;
+ + " to " + ProcessList.makeProcStateString(app.getCurProcState())
+ + " (" + app.getCurProcState() + ")" + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
- boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE;
+ boolean curImportant = app.getCurProcState() < ActivityManager.PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
- // This app is no longer something we consider important enough to allow to
- // use arbitrary amounts of battery power. Note
- // its current CPU time to later know to kill it if
- // it is not behaving well.
- app.whenUnimportant = now;
+ // This app is no longer something we consider important enough to allow to use
+ // arbitrary amounts of battery power. Note its current CPU time to later know to
+ // kill it if it is not behaving well.
+ app.setWhenUnimportant(now);
app.lastCpuTime = 0;
}
// Inform UsageStats of important process state change
@@ -18544,7 +16667,7 @@
maybeUpdateLastTopTime(app, now);
- app.setProcState = app.curProcState;
+ app.setProcState = app.getCurProcState();
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
app.notCachedSinceIdle = false;
}
@@ -18553,7 +16676,7 @@
} else {
app.procStateChanged = true;
}
- } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime)
+ } else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
> mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
// For apps that sit around for a long time in the interactive state, we need
// to report this at least once a day so they don't go idle.
@@ -18707,7 +16830,7 @@
private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
String authority) {
if (app == null) return;
- if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
UserState userState = mUserController.getStartedUserState(app.userId);
if (userState == null) return;
final long now = SystemClock.elapsedRealtime();
@@ -18727,7 +16850,7 @@
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+ "] state changes: old = " + app.setProcState + ", new = "
- + app.curProcState);
+ + app.getCurProcState());
}
if (mUsageStatsService == null) {
return;
@@ -18736,24 +16859,26 @@
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
// is sleeping, nor do short foreground services.
- if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
isInteraction = true;
- app.fgInteractionTime = 0;
- } else if (app.curProcState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- if (app.fgInteractionTime == 0) {
- app.fgInteractionTime = nowElapsed;
+ app.setFgInteractionTime(0);
+ } else if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ if (app.getFgInteractionTime() == 0) {
+ app.setFgInteractionTime(nowElapsed);
isInteraction = false;
} else {
- isInteraction = nowElapsed > app.fgInteractionTime
+ isInteraction = nowElapsed > app.getFgInteractionTime()
+ mConstants.SERVICE_USAGE_INTERACTION_TIME;
}
} else {
- isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
- app.fgInteractionTime = 0;
+ isInteraction =
+ app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ app.setFgInteractionTime(0);
}
- if (isInteraction && (!app.reportedInteraction || (nowElapsed-app.interactionEventTime)
+ if (isInteraction
+ && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
> mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
- app.interactionEventTime = nowElapsed;
+ app.setInteractionEventTime(nowElapsed);
String[] packages = app.getPackageList();
if (packages != null) {
for (int i = 0; i < packages.length; i++) {
@@ -18764,13 +16889,13 @@
}
app.reportedInteraction = isInteraction;
if (!isInteraction) {
- app.interactionEventTime = 0;
+ app.setInteractionEventTime(0);
}
}
private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
- && app.curProcState > ActivityManager.PROCESS_STATE_TOP) {
+ && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
app.lastTopTime = nowUptime;
}
}
@@ -18830,13 +16955,14 @@
// TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
// the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
- private ActivityRecord resumedAppLocked() {
- final ActivityRecord act = mStackSupervisor.getTopResumedActivity();
+ ProcessRecord getTopAppLocked() {
+ final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null;
+ final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null;
String pkg;
int uid;
- if (act != null) {
- pkg = act.packageName;
- uid = act.info.applicationInfo.uid;
+ if (r != null) {
+ pkg = r.processName;
+ uid = r.info.uid;
} else {
pkg = null;
uid = -1;
@@ -18862,7 +16988,7 @@
}
}
- return act;
+ return r;
}
/**
@@ -18874,9 +17000,7 @@
*/
@GuardedBy("this")
final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
- final ActivityRecord TOP_ACT = resumedAppLocked();
- final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
- ? (ProcessRecord) TOP_ACT.app.mOwner : null;
+ final ProcessRecord TOP_APP = getTopAppLocked();
final boolean wasCached = app.cached;
mAdjSeq++;
@@ -18885,12 +17009,12 @@
// If our app is currently cached, we know it, and that is it. Otherwise,
// we don't know it yet, and it needs to now be cached we will then
// need to do a complete oom adj.
- final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ
- ? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
+ final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+ ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
SystemClock.uptimeMillis());
if (oomAdjAll
- && (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ)) {
+ && (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
updateOomAdjLocked();
@@ -18899,23 +17023,13 @@
}
@GuardedBy("this")
- ProcessRecord getTopAppLocked() {
- final ActivityRecord TOP_ACT = resumedAppLocked();
- if (TOP_ACT != null && TOP_ACT.hasProcess()) {
- return (ProcessRecord) TOP_ACT.app.mOwner;
- } else {
- return null;
- }
- }
-
- @GuardedBy("this")
final void updateOomAdjLocked() {
mOomAdjProfiler.oomAdjStarted();
final ProcessRecord TOP_APP = getTopAppLocked();
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
- final int N = mLruProcesses.size();
+ final int N = mProcessList.getLruSizeLocked();
// Reset state in all uid records.
for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -18925,8 +17039,8 @@
uidRec.reset();
}
- if (mStackSupervisor != null) {
- mStackSupervisor.rankTaskLayersIfNeeded();
+ if (mAtmInternal != null) {
+ mAtmInternal.rankTaskLayersIfNeeded();
}
mAdjSeq++;
@@ -18976,11 +17090,11 @@
// need to reset cycle state before calling computeOomAdjLocked because of service connections
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
app.containsCycle = false;
}
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
@@ -18991,14 +17105,14 @@
// If we haven't yet assigned the final cached adj
// to the process, do that now.
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
- switch (app.curProcState) {
+ switch (app.getCurProcState()) {
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// This process is a cached process holding activities...
// assign it the next cached value for that type, and then
// step that cached level.
- app.curRawAdj = curCachedAdj;
+ app.setCurRawAdj(curCachedAdj);
app.curAdj = app.modifyRawOomAdj(curCachedAdj);
if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
@@ -19021,7 +17135,7 @@
// long-running services that have dropped down to the
// cached level will be treated as empty (since their process
// state is still as a service), which is what we want.
- app.curRawAdj = curEmptyAdj;
+ app.setCurRawAdj(curEmptyAdj);
app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
@@ -19055,7 +17169,7 @@
retryCycles = false;
for (int i=0; i<N; i++) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
app.adjSeq--;
app.completedAdjSeq--;
@@ -19063,7 +17177,7 @@
}
for (int i=0; i<N; i++) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
@@ -19074,12 +17188,12 @@
}
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
applyOomAdjLocked(app, true, now, nowElapsed);
// Count the number of process types.
- switch (app.curProcState) {
+ switch (app.getCurProcState()) {
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
@@ -19120,8 +17234,8 @@
final UidRecord uidRec = app.uidRecord;
if (uidRec != null) {
uidRec.ephemeral = app.info.isInstantApp();
- if (uidRec.curProcState > app.curProcState) {
- uidRec.curProcState = app.curProcState;
+ if (uidRec.curProcState > app.getCurProcState()) {
+ uidRec.curProcState = app.getCurProcState();
}
if (app.hasForegroundServices()) {
uidRec.foregroundServices = true;
@@ -19129,7 +17243,7 @@
}
}
- if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
+ if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
&& !app.killedByAm) {
numTrimming++;
}
@@ -19167,9 +17281,9 @@
// has gone down since last time.
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
- + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);
+ + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
if (memFactor > mLastMemoryLevel) {
- if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {
+ if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
memFactor = mLastMemoryLevel;
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
}
@@ -19179,7 +17293,7 @@
StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
}
mLastMemoryLevel = memFactor;
- mLastNumProcesses = mLruProcesses.size();
+ mLastNumProcesses = mProcessList.getLruSizeLocked();
boolean allChanged = mProcessStats.setMemFactorLocked(
memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
final int trackerMemFactor = mProcessStats.getMemFactorLocked();
@@ -19207,12 +17321,12 @@
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
- if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
+ if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
&& !app.killedByAm) {
if (app.trimMemoryLevel < curLevel && app.thread != null) {
try {
@@ -19235,7 +17349,7 @@
break;
}
}
- } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
&& !app.killedByAm) {
if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
&& app.thread != null) {
@@ -19250,8 +17364,8 @@
}
app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
} else {
- if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.pendingUiClean) {
+ if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || app.systemNoUi) && app.hasPendingUiClean()) {
// If this application is now in the background and it
// had done UI, then give it the special trim level to
// have it free UI resources.
@@ -19265,7 +17379,7 @@
} catch (RemoteException e) {
}
}
- app.pendingUiClean = false;
+ app.setPendingUiClean(false);
}
if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
try {
@@ -19285,13 +17399,13 @@
mLowRamStartTime = 0;
}
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
- if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.pendingUiClean) {
+ if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || app.systemNoUi) && app.hasPendingUiClean()) {
if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
&& app.thread != null) {
try {
@@ -19303,7 +17417,7 @@
} catch (RemoteException e) {
}
}
- app.pendingUiClean = false;
+ app.setPendingUiClean(false);
}
app.trimMemoryLevel = 0;
}
@@ -19312,7 +17426,7 @@
if (mAlwaysFinishActivities) {
// Need to do this on its own message because the stack may not
// be in a consistent state at this point.
- mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
+ mAtmInternal.scheduleDestroyAllActivities("always-finish");
}
if (allChanged) {
@@ -19549,7 +17663,7 @@
continue;
}
synchronized (uidRec.networkStateLock) {
- uidRec.curProcStateSeq = ++mProcStateSeqCounter;
+ uidRec.curProcStateSeq = ++mProcessList.mProcStateSeqCounter; // TODO: use method
if (blockState == NETWORK_STATE_BLOCK) {
if (blockingUids == null) {
blockingUids = new ArrayList<>();
@@ -19572,8 +17686,8 @@
return;
}
- for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
- final ProcessRecord app = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; --i) {
+ final ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!blockingUids.contains(app.uid)) {
continue;
}
@@ -19771,8 +17885,8 @@
final void trimApplicationsLocked() {
// First remove any unused application processes whose package
// has been removed.
- for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
- final ProcessRecord app = mRemovedProcesses.get(i);
+ for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
if (!app.hasActivitiesOrRecentTasks()
&& app.curReceivers.isEmpty() && app.services.size() == 0) {
Slog.i(
@@ -19790,7 +17904,7 @@
}
}
cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
- mRemovedProcesses.remove(i);
+ mProcessList.mRemovedProcesses.remove(i);
if (app.isPersistent()) {
addAppLocked(app.info, null, false, null /* ABI override */);
@@ -19816,8 +17930,8 @@
+ android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
}
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = mProcessList.mLruProcesses.get(i);
if (r.thread != null && r.isPersistent()) {
sendSignal(r.pid, sig);
}
@@ -19944,7 +18058,7 @@
if (proc == null) {
ArrayMap<String, SparseArray<ProcessRecord>> all
- = mProcessNames.getMap();
+ = mProcessList.mProcessNames.getMap();
SparseArray<ProcessRecord> procs = all.get(process);
if (procs != null && procs.size() > 0) {
proc = procs.valueAt(0);
@@ -20074,15 +18188,8 @@
}
void onCoreSettingsChange(Bundle settings) {
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord processRecord = mLruProcesses.get(i);
- try {
- if (processRecord.thread != null) {
- processRecord.thread.setCoreSettings(settings);
- }
- } catch (RemoteException re) {
- /* ignore */
- }
+ synchronized (this) {
+ mProcessList.updateCoreSettingsLocked(settings);
}
}
@@ -20214,8 +18321,8 @@
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- for (int i = 0; i < mLruProcesses.size(); i++) {
- ProcessRecord process = mLruProcesses.get(i);
+ for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+ ProcessRecord process = mProcessList.mLruProcesses.get(i);
if (!processSanityChecksLocked(process)) {
continue;
}
@@ -20247,7 +18354,7 @@
PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
pw.println("Binder transaction traces for all processes.\n");
- for (ProcessRecord process : mLruProcesses) {
+ for (ProcessRecord process : mProcessList.mLruProcesses) {
if (!processSanityChecksLocked(process)) {
continue;
}
@@ -20308,9 +18415,10 @@
public void killForegroundAppsForUser(int userHandle) {
synchronized (ActivityManagerService.this) {
final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final SparseArray<ProcessRecord> apps =
+ mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
@@ -20320,7 +18428,7 @@
}
if (app.removed) {
procs.add(app);
- } else if (app.userId == userHandle && app.foregroundActivities) {
+ } else if (app.userId == userHandle && app.hasForegroundActivities()) {
app.removed = true;
procs.add(app);
}
@@ -20329,7 +18437,7 @@
final int N = procs.size();
for (int i = 0; i < N; i++) {
- removeProcessLocked(procs.get(i), false, true, "kill all fg");
+ mProcessList.removeProcessLocked(procs.get(i), false, true, "kill all fg");
}
}
}
@@ -20391,10 +18499,10 @@
return;
}
}
- if (pr.hasOverlayUi == hasOverlayUi) {
+ if (pr.hasOverlayUi() == hasOverlayUi) {
return;
}
- pr.hasOverlayUi = hasOverlayUi;
+ pr.setHasOverlayUi(hasOverlayUi);
//Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
updateOomAdjLocked(pr, true);
}
@@ -20450,36 +18558,6 @@
}
@Override
- public void saveANRState(String reason) {
- synchronized (ActivityManagerService.this) {
- final StringWriter sw = new StringWriter();
- final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
- pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
- if (reason != null) {
- pw.println(" Reason: " + reason);
- }
- pw.println();
- mActivityTaskManager.getActivityStartController().dump(pw, " ", null);
- pw.println();
- pw.println("-------------------------------------------------------------------------------");
- dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
- true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
- "" /* header */);
- pw.println();
- pw.close();
-
- mLastANRState = sw.toString();
- }
- }
-
- @Override
- public void clearSavedANRState() {
- synchronized (ActivityManagerService.this) {
- mLastANRState = null;
- }
- }
-
- @Override
public boolean isRuntimeRestarted() {
return mSystemServiceManager.isRuntimeRestarted();
}
@@ -20545,6 +18623,28 @@
}
@Override
+ public List<ProcessMemoryState> getMemoryStateForNativeProcesses() {
+ List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
+ int[] pids = getPidsForCommands(MEMORY_STAT_INTERESTING_NATIVE_PROCESSES);
+ for (int i = 0; i < pids.length; i++) {
+ int pid = pids[i];
+ MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
+ if (memoryStat == null) {
+ continue;
+ }
+ int uid = getUidForPid(pid);
+ String processName = readCmdlineFromProcfs(pid);
+ int oomScore = -1; // Unused, not included in the NativeProcessMemoryState atom.
+ ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, processName,
+ oomScore, memoryStat.pgfault, memoryStat.pgmajfault,
+ memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes,
+ memoryStat.rssHighWatermarkInBytes);
+ processMemoryStates.add(processMemoryState);
+ }
+ return processMemoryStates;
+ }
+
+ @Override
public int handleIncomingUser(int callingPid, int callingUid, int userId,
boolean allowAll, int allowMode, String name, String callerPackage) {
return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
@@ -20572,10 +18672,6 @@
ActivityManagerService.this.trimApplications();
}
- public void closeSystemDialogs(String reason) {
- ActivityManagerService.this.closeSystemDialogs(reason);
- }
-
public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
synchronized (ActivityManagerService.this) {
for (int i = 0; i < procsToKill.size(); i++) {
@@ -20599,8 +18695,8 @@
if (packageName == null) return false;
synchronized (ActivityManagerService.this) {
- for (int i = 0; i < mLruProcesses.size(); i++) {
- final ProcessRecord pr = mLruProcesses.get(i);
+ for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+ final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
if (pr.uid != uid) {
continue;
}
@@ -20779,6 +18875,223 @@
(ConnectionRecord) cr, null, c));
}
}
+
+ public void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
+ synchronized(ActivityManagerService.this) {
+ mServices.cleanUpServices(userId, component, baseIntent);
+ }
+ }
+
+ public ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+ // Locked intentionally not held as it isn't needed for this case.
+ return ActivityManagerService.this.getActivityInfoForUser(aInfo, userId);
+ }
+
+ public void ensureBootCompleted() {
+ // Locked intentionally not held as it isn't needed for this case.
+ ActivityManagerService.this.ensureBootCompleted();
+ }
+
+ public void updateOomLevelsForDisplay(int displayId) {
+ synchronized(ActivityManagerService.this) {
+ if (mWindowManager != null) {
+ mProcessList.applyDisplaySize(mWindowManager);
+ }
+ }
+ }
+
+ public boolean isActivityStartsLoggingEnabled() {
+ return mConstants.mFlagActivityStartsLoggingEnabled;
+ }
+
+ public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
+ synchronized(ActivityManagerService.this) {
+ ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
+ ? UsageEvents.Event.KEYGUARD_SHOWN
+ : UsageEvents.Event.KEYGUARD_HIDDEN);
+ }
+ }
+
+ @Override
+ public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
+ synchronized (ActivityManagerService.this) {
+ return ActivityManagerService.this.inputDispatchingTimedOut(
+ pid, aboveSystem, reason);
+ }
+ }
+
+ @Override
+ public boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
+ ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
+ boolean aboveSystem, String reason) {
+ return ActivityManagerService.this.inputDispatchingTimedOut((ProcessRecord) proc,
+ activityShortComponentName, aInfo, parentShortComponentName,
+ (WindowProcessController) parentProc, aboveSystem, reason);
+
+ }
+
+ @Override
+ public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
+ synchronized (ActivityManagerService.this) {
+ Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+ intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ if (initLocale || !mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+
+ // Send a broadcast to PackageInstallers if the configuration change is interesting
+ // for the purposes of installing additional splits.
+ if (!initLocale && isSplitConfigurationChange(changes)) {
+ intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+ // Typically only app stores will have this permission.
+ String[] permissions =
+ new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
+ permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this configuration change is interesting enough to send an
+ * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+ */
+ private boolean isSplitConfigurationChange(int configDiff) {
+ return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+ }
+
+ @Override
+ public void broadcastCloseSystemDialogs(String reason) {
+ synchronized (ActivityManagerService.this) {
+ final Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ if (reason != null) {
+ intent.putExtra("reason", reason);
+ }
+
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+ }
+
+ @Override
+ public void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.killAllBackgroundProcessesExcept(
+ minTargetSdk, maxProcState);
+ }
+ }
+
+ @Override
+ public void startProcess(String processName, ApplicationInfo info,
+ boolean knownToBeDead, String hostingType, ComponentName hostingName) {
+ synchronized (ActivityManagerService.this) {
+ startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
+ hostingType, hostingName, false /* allowWhileBooting */,
+ false /* isolated */, true /* keepIfLarge */);
+ }
+ }
+
+ @Override
+ public void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+ ProfilerInfo profilerInfo) {
+ synchronized (ActivityManagerService.this) {
+ if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
+ setDebugApp(aInfo.processName, true, false);
+ }
+
+ if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
+ setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+ }
+
+ if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
+ setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
+ }
+
+ if (profilerInfo != null) {
+ setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+ }
+ }
+ }
+ }
+
+ long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
+ if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission " + FILTER_EVENTS);
+ }
+ ProcessRecord proc;
+ long timeout;
+ synchronized (this) {
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
+ }
+
+ if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
+ return -1;
+ }
+
+ return timeout;
+ }
+
+ /**
+ * Handle input dispatching timeouts.
+ * @return whether input dispatching should be aborted or not.
+ */
+ boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
+ ApplicationInfo aInfo, String parentShortComponentName,
+ WindowProcessController parentProcess, boolean aboveSystem, String reason) {
+ if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission " + FILTER_EVENTS);
+ }
+
+ final String annotation;
+ if (reason == null) {
+ annotation = "Input dispatching timed out";
+ } else {
+ annotation = "Input dispatching timed out (" + reason + ")";
+ }
+
+ if (proc != null) {
+ synchronized (this) {
+ if (proc.isDebugging()) {
+ return false;
+ }
+
+ if (proc.getActiveInstrumentation() != null) {
+ Bundle info = new Bundle();
+ info.putString("shortMsg", "keyDispatchingTimedOut");
+ info.putString("longMsg", annotation);
+ finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
+ return true;
+ }
+ }
+ proc.appNotResponding(activityShortComponentName, aInfo,
+ parentShortComponentName, parentProcess, aboveSystem, annotation);
+ }
+
+ return true;
}
/**
@@ -20901,8 +19214,8 @@
}
try {
synchronized(this) {
- killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid), userId,
- ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
+ mProcessList.killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid),
+ userId, ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
"dep: " + packageName);
}
} finally {
@@ -20932,33 +19245,9 @@
void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
final boolean updateFrameworkRes = packagesToUpdate.contains("android");
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- final ProcessRecord app = mLruProcesses.get(i);
- if (app.thread == null) {
- continue;
- }
- if (userId != UserHandle.USER_ALL && app.userId != userId) {
- continue;
- }
+ mProcessList.updateApplicationInfoLocked(packagesToUpdate, userId, updateFrameworkRes);
- final int packageCount = app.pkgList.size();
- for (int j = 0; j < packageCount; j++) {
- final String packageName = app.pkgList.keyAt(j);
- if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
- try {
- final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
- if (ai != null) {
- app.thread.scheduleApplicationInfoChanged(ai);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
- packageName, app));
- }
- }
- }
- }
if (updateFrameworkRes) {
// Update system server components that need to know about changed overlays. Because the
// overlay is applied in ActivityThread, we need to serialize through its thread too.
@@ -20980,7 +19269,8 @@
public void attachAgent(String process, String path) {
try {
synchronized (this) {
- ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent");
+ ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM,
+ "attachAgent");
if (proc == null || proc.thread == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 40c555f8..96601a2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -17,14 +17,13 @@
package com.android.server.am;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -481,12 +480,12 @@
options.setLockTaskEnabled(true);
}
if (mWaitOption) {
- result = mTaskInterface.startActivityAndWait(null, null, intent, mimeType,
+ result = mInternal.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
res = result.result;
} else {
- res = mTaskInterface.startActivityAsUser(null, null, intent, mimeType,
+ res = mInternal.startActivityAsUser(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
}
@@ -2902,6 +2901,7 @@
pw.println(" --receiver-permission <PERMISSION>: Require receiver to hold permission.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current] [--no-hidden-api-checks]");
+ pw.println(" [--no-isolated-storage]");
pw.println(" [--no-window-animation] [--abi <ABI>] <COMPONENT>");
pw.println(" Start an Instrumentation. Typically this target <COMPONENT> is in the");
pw.println(" form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
@@ -2920,6 +2920,8 @@
pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
pw.println(" current user if not specified.");
pw.println(" --no-hidden-api-checks: disable restrictions on use of hidden API.");
+ pw.println(" --no-isolated-storage: don't use isolated storage sandbox and ");
+ pw.println(" mount full external storage");
pw.println(" --no-window-animation: turn off window animations while running.");
pw.println(" --abi <ABI>: Launch the instrumented process with the selected ABI.");
pw.println(" This assumes that the process supports the selected ABI.");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 7080e2b..a0dd878 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -72,9 +72,9 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
@@ -114,7 +114,7 @@
*/
class ActivityMetricsLogger {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_ATM;
// Window modes we are interested in logging. If we ever introduce a new type, we need to add
// a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array.
@@ -155,7 +155,6 @@
private final H mHandler;
private ArtManagerInternal mArtManagerInternal;
- private boolean mDrawingTraceActive;
private final StringBuilder mStringBuilder = new StringBuilder();
private final class H extends Handler {
@@ -230,14 +229,14 @@
launchedActivityLaunchToken = launchedActivity.info.launchToken;
launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
? null
- : info.launchedActivity.app.getRequiredAbi();
+ : launchedActivity.app.getRequiredAbi();
reason = info.reason;
startingWindowDelayMs = info.startingWindowDelayMs;
bindApplicationDelayMs = info.bindApplicationDelayMs;
windowsDrawnDelayMs = info.windowsDrawnDelayMs;
type = getTransitionType(info);
- processRecord = findProcessForActivity(info.launchedActivity);
- processName = info.launchedActivity.processName;
+ processRecord = findProcessForActivity(launchedActivity);
+ processName = launchedActivity.processName;
userId = launchedActivity.userId;
launchedActivityShortComponentName = launchedActivity.shortComponentName;
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
@@ -501,7 +500,6 @@
if (mWindowingModeTransitionInfo.size() == 0) {
reset(true /* abort */, info);
}
- stopFullyDrawnTraceIfNeeded();
}
}
}
@@ -509,14 +507,14 @@
/**
* Notifies the tracker that we called immediately before we call bindApplication on the client.
*
- * @param app The client into which we'll call bindApplication.
+ * @param appInfo The client into which we'll call bindApplication.
*/
- void notifyBindApplication(ProcessRecord app) {
+ void notifyBindApplication(ApplicationInfo appInfo) {
for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
// App isn't attached to record yet, so match with info.
- if (info.launchedActivity.appInfo == app.info) {
+ if (info.launchedActivity.appInfo == appInfo) {
info.bindApplicationDelayMs = calculateCurrentDelay();
}
}
@@ -699,6 +697,13 @@
if (info == null) {
return null;
}
+
+ // Record the handling of the reportFullyDrawn callback in the trace system. This is not
+ // actually used to trace this function, but instead the logical task that this function
+ // fullfils (handling reportFullyDrawn() callbacks).
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "ActivityManager:ReportingFullyDrawn " + info.launchedActivity.packageName);
+
final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
builder.setPackageName(r.packageName);
builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
@@ -720,7 +725,11 @@
info.launchedActivity.info.name,
info.currentTransitionProcessRunning,
startupTimeMs);
- stopFullyDrawnTraceIfNeeded();
+
+ // Ends the trace started at the beginning of this function. This is located here to allow
+ // the trace slice to have a noticable duration.
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
@@ -741,7 +750,7 @@
Log.i(TAG, sb.toString());
}
- void logActivityStart(Intent intent, ProcessRecord callerApp, ActivityRecord r,
+ void logActivityStart(Intent intent, WindowProcessController callerApp, ActivityRecord r,
int callingUid, String callingPackage, int callingUidProcState,
boolean callingUidHasAnyVisibleWindow,
int realCallingUid, int realCallingUidProcState,
@@ -776,31 +785,31 @@
builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
if (callerApp != null) {
- builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.processName);
+ builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
- processStateAmToProto(callerApp.curProcState));
+ processStateAmToProto(callerApp.getCurrentProcState()));
builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES,
- callerApp.hasClientActivities ? 1 : 0);
+ callerApp.hasClientActivities() ? 1 : 0);
builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES,
callerApp.hasForegroundServices() ? 1 : 0);
builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES,
- callerApp.foregroundActivities ? 1 : 0);
- builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi ? 1 : 0);
+ callerApp.hasForegroundActivities() ? 1 : 0);
+ builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi() ? 1 : 0);
builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_OVERLAY_UI,
- callerApp.hasOverlayUi ? 1 : 0);
+ callerApp.hasOverlayUi() ? 1 : 0);
builder.addTaggedData(FIELD_PROCESS_RECORD_PENDING_UI_CLEAN,
- callerApp.pendingUiClean ? 1 : 0);
- if (callerApp.interactionEventTime != 0) {
+ callerApp.hasPendingUiClean() ? 1 : 0);
+ if (callerApp.getInteractionEventTime() != 0) {
builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT,
- (nowElapsed - callerApp.interactionEventTime));
+ (nowElapsed - callerApp.getInteractionEventTime()));
}
- if (callerApp.fgInteractionTime != 0) {
+ if (callerApp.getFgInteractionTime() != 0) {
builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION,
- (nowElapsed - callerApp.fgInteractionTime));
+ (nowElapsed - callerApp.getFgInteractionTime()));
}
- if (callerApp.whenUnimportant != 0) {
+ if (callerApp.getWhenUnimportant() != 0) {
builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT,
- (nowUptime - callerApp.whenUnimportant));
+ (nowUptime - callerApp.getWhenUnimportant()));
}
}
builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
@@ -887,10 +896,7 @@
}
/**
- * Starts traces for app launch and draw times. We stop the fully drawn trace if its already
- * active since the app may not have reported fully drawn in the previous launch.
- *
- * See {@link android.app.Activity#reportFullyDrawn()}
+ * Starts traces for app launch.
*
* @param info
* */
@@ -898,14 +904,11 @@
if (info == null) {
return;
}
- stopFullyDrawnTraceIfNeeded();
int transitionType = getTransitionType(info);
if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
|| transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
+ info.launchedActivity.packageName, 0);
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
- mDrawingTraceActive = true;
info.launchTraceActive = true;
}
}
@@ -920,11 +923,4 @@
info.launchTraceActive = false;
}
}
-
- void stopFullyDrawnTraceIfNeeded() {
- if (mDrawingTraceActive) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
- mDrawingTraceActive = false;
- }
- }
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6bdceb2..865c774 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -81,22 +81,23 @@
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
@@ -114,11 +115,14 @@
import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
import static com.android.server.am.TaskPersister.DEBUG;
import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -211,7 +215,7 @@
* An entry in the history stack, representing an activity.
*/
final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
private static final String TAG_STATES = TAG + POSTFIX_STATES;
@@ -338,12 +342,6 @@
int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
- // This activity is not being relaunched, or being relaunched for a non-resize reason.
- static final int RELAUNCH_REASON_NONE = 0;
- // This activity is being relaunched due to windowing mode change.
- static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
- // This activity is being relaunched due to a free-resize operation.
- static final int RELAUNCH_REASON_FREE_RESIZE = 2;
// Marking the reason why this activity is being relaunched. Mainly used to track that this
// activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
// pre-NYC apps that don't have a sense of being resized.
@@ -852,13 +850,12 @@
}
}
- ActivityRecord(ActivityTaskManagerService _service, ProcessRecord _caller, int _launchedFromPid,
- int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
- ActivityInfo aInfo, Configuration _configuration,
- ActivityRecord _resultTo, String _resultWho, int _reqCode,
- boolean _componentSpecified, boolean _rootVoiceInteraction,
- ActivityStackSupervisor supervisor, ActivityOptions options,
- ActivityRecord sourceRecord) {
+ ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
+ int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
+ String _resolvedType, ActivityInfo aInfo, Configuration _configuration,
+ ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
+ boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
+ ActivityOptions options, ActivityRecord sourceRecord) {
service = _service;
appToken = new Token(this, _intent);
info = aInfo;
@@ -928,8 +925,8 @@
}
if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
&& (aInfo.applicationInfo.uid == SYSTEM_UID
- || aInfo.applicationInfo.uid == _caller.info.uid)) {
- processName = _caller.processName;
+ || aInfo.applicationInfo.uid == _caller.mInfo.uid)) {
+ processName = _caller.mName;
} else {
processName = aInfo.processName;
}
@@ -1783,7 +1780,7 @@
}
setVisible(true);
sleeping = false;
- app.setPendingUiClean(true);
+ app.postPendingUiCleanMsg(true);
if (reportToClient) {
makeClientVisible();
} else {
@@ -2115,12 +2112,16 @@
windowFromSameProcessAsActivity =
!hasProcess() || app.getPid() == windowPid || windowPid == -1;
}
+
if (windowFromSameProcessAsActivity) {
- return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
+ return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+ anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
+ app, false, reason);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
- return service.inputDispatchingTimedOut(windowPid, false /* aboveSystem */, reason) < 0;
+ return service.mAmInternal.inputDispatchingTimedOut(
+ windowPid, false /* aboveSystem */, reason) < 0;
}
}
@@ -2212,12 +2213,13 @@
}
/**
- * @return display id to which this record is attached, -1 if not attached.
+ * @return display id to which this record is attached,
+ * {@link android.view.Display#INVALID_DISPLAY} if not attached.
*/
int getDisplayId() {
final ActivityStack stack = getStack();
if (stack == null) {
- return -1;
+ return INVALID_DISPLAY;
}
return stack.mDisplayId;
}
@@ -2282,6 +2284,11 @@
// We don't show starting window for overlay activities.
return;
}
+ if (pendingOptions != null
+ && pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+ // Don't show starting window when using shared element transition.
+ return;
+ }
final CompatibilityInfo compatInfo =
service.compatibilityInfoForPackageLocked(info.applicationInfo);
@@ -3007,17 +3014,6 @@
mWindowContainerController.registerRemoteAnimations(definition);
}
- static String relaunchReasonToString(int relaunchReason) {
- switch (relaunchReason) {
- case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
- return "window_resize";
- case RELAUNCH_REASON_FREE_RESIZE:
- return "free_resize";
- default:
- return null;
- }
- }
-
@Override
public String toString() {
if (stringName != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 864bf2d..026c5cc 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -48,41 +48,41 @@
import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.am.ActivityDisplay.POSITION_TOP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TRANSITION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TRANSITION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -103,6 +103,7 @@
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.am.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static java.lang.Integer.MAX_VALUE;
import android.app.Activity;
@@ -172,7 +173,7 @@
*/
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
implements StackWindowListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_APP = TAG + POSTFIX_APP;
private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
@@ -364,12 +365,12 @@
private boolean mTopActivityOccludesKeyguard;
private ActivityRecord mTopDismissingKeyguardActivity;
- static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
- static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
- static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
- static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
- static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
- static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
+ static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+ static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
+ static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
+ static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
+ static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
+ static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
private static class ScheduleDestroyArgs {
final WindowProcessController mOwner;
@@ -1509,8 +1510,6 @@
prev.getTask().touchActiveTime();
clearLaunchTime(prev);
- mStackSupervisor.getActivityMetricsLogger().stopFullyDrawnTraceIfNeeded();
-
mService.updateCpuStats();
if (prev.attachedToProcess()) {
@@ -3878,8 +3877,8 @@
// The activity that we are finishing may be over the lock screen. In this case, we do not
// want to consider activities that cannot be shown on the lock screen as running and should
// proceed with finishing the activity if there is no valid next top running activity.
- final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */);
+ final ActivityDisplay display = getDisplay();
+ final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible) {
@@ -3903,23 +3902,25 @@
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
r.setState(FINISHING, "finishCurrentActivityLocked");
- final boolean finishingActivityInNonFocusedStack
- = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
- && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
+ final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
+ && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+ || (next == null && display.topRunningActivity() == null));
if (mode == FINISH_IMMEDIATELY
|| (prevState == PAUSED
&& (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
- || finishingActivityInNonFocusedStack
+ || finishingInNonFocusedStackOrNoRunning
|| prevState == STOPPING
|| prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
- if (finishingActivityInNonFocusedStack) {
+ if (finishingInNonFocusedStackOrNoRunning) {
// Finishing activity that was in paused state and it was in not currently focused
- // stack, need to make something visible in its place.
+ // stack, need to make something visible in its place. Also if the display does not
+ // have running activity, the configuration may need to be updated for restoring
+ // original orientation of the display.
mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8c8146c..257a004 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,8 +21,12 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_DEBUG;
+import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WaitResult.INVALID_DELAY;
@@ -54,28 +58,6 @@
import static android.view.Display.TYPE_VIRTUAL;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
-import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_NONE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
@@ -91,7 +73,28 @@
import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.am.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -113,6 +116,7 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
+import android.app.IApplicationThread;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
@@ -198,7 +202,7 @@
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
RecentTasks.Callbacks, RootWindowContainerListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -789,8 +793,9 @@
}
boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
- if (displayId == DEFAULT_DISPLAY) {
- // No restrictions to default display.
+ if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+ && displayId == mService.mVr2dDisplayId)) {
+ // No restrictions to default display or vr 2d display.
return true;
}
@@ -994,8 +999,8 @@
return candidateTaskId;
}
- boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
- final String processName = app.processName;
+ boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
+ final String processName = app.mName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
@@ -1009,7 +1014,7 @@
final int size = mTmpActivityList.size();
for (int i = 0; i < size; i++) {
final ActivityRecord activity = mTmpActivityList.get(i);
- if (activity.app == null && app.uid == activity.info.applicationInfo.uid
+ if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
&& processName.equals(activity.processName)) {
try {
if (realStartActivityLocked(activity, app,
@@ -1209,75 +1214,15 @@
}
ActivityRecord topRunningActivityLocked() {
- return topRunningActivityLocked(false /* considerKeyguardState */);
- }
-
- /**
- * Returns the top running activity in the focused stack. In the case the focused stack has no
- * such activity, the next focusable stack on top of a display is returned.
- * @param considerKeyguardState Indicates whether the locked state should be considered. if
- * {@code true} and the keyguard is locked, only activities that
- * can be shown on top of the keyguard will be considered.
- * @return The top running activity. {@code null} if none is available.
- */
- ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- ActivityRecord r = focusedStack.topRunningActivityLocked();
- if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
- return r;
- }
-
- // Look in other non-focused and non-home stacks.
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
-
- // TODO: We probably want to consider the top fullscreen stack as we could have a pinned
- // stack on top.
- final ActivityStack topStack = display.getTopStack();
-
- // Only consider focusable top stacks other than the current focused one.
- if (topStack == null || !topStack.isFocusable() || topStack == focusedStack) {
- continue;
- }
-
- final ActivityRecord topActivity = topStack.topRunningActivityLocked();
-
- // Skip if no top activity.
- if (topActivity == null) {
- continue;
- }
-
-
- // This activity can be considered the top running activity if we are not
- // considering the locked state, the keyguard isn't locked, or we can show when
- // locked.
- if (isValidTopRunningActivity(topActivity, considerKeyguardState)) {
+ final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+ if (topActivity != null) {
return topActivity;
}
}
-
return null;
}
- /**
- * Verifies an {@link ActivityRecord} can be the top activity based on keyguard state and
- * whether we are considering it.
- */
- private boolean isValidTopRunningActivity(ActivityRecord record,
- boolean considerKeyguardState) {
- if (!considerKeyguardState) {
- return true;
- }
-
- final boolean keyguardLocked = getKeyguardController().isKeyguardLocked();
-
- if (!keyguardLocked) {
- return true;
- }
-
- return record.canShowWhenLocked();
- }
-
@VisibleForTesting
void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
@ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
@@ -1299,20 +1244,17 @@
// Don't debug things in the system process
if (!aInfo.processName.equals("system")) {
- if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
- mService.mAm.setDebugApp(aInfo.processName, true, false);
- }
-
- if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
- mService.mAm.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
- }
-
- if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
- mService.mAm.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
- }
-
- if (profilerInfo != null) {
- mService.mAm.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+ if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
+ | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
+ /**
+ * Assume safe to call into AMS synchronously because the call that set these
+ * flags should have originated from AMS which will already have its lock held.
+ * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String,
+ * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
+ * TODO(b/80414790): Investigate a better way of untangling this.
+ */
+ mService.mAmInternal.setDebugFlagsForStartingActivity(
+ aInfo, startFlags, profilerInfo);
}
}
final String intentLaunchToken = intent.getLaunchToken();
@@ -1359,7 +1301,7 @@
return resolveActivity(intent, rInfo, startFlags, profilerInfo);
}
- final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
+ private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
if (!allPausedActivitiesComplete()) {
@@ -1378,7 +1320,6 @@
beginDeferResume();
try {
- final WindowProcessController proc = app.getWindowProcessController();
r.startFreezingScreenLocked(proc, 0);
// schedule launch ticks to collect information about slow apps.
@@ -1414,15 +1355,15 @@
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
- if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
+ if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
Slog.wtf(TAG,
"User ID for activity changing for " + r
+ " appInfo.uid=" + r.appInfo.uid
+ " info.ai.uid=" + applicationInfoUid
- + " old=" + r.app + " new=" + app);
+ + " old=" + r.app + " new=" + proc);
}
- app.waitingToKill = null;
+ proc.clearWaitingToKill();
r.launchCount++;
r.lastLaunchTime = SystemClock.uptimeMillis();
@@ -1441,7 +1382,7 @@
}
try {
- if (app.thread == null) {
+ if (!proc.hasThread()) {
throw new RemoteException();
}
List<ResultInfo> results = null;
@@ -1467,50 +1408,29 @@
r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
- ProfilerInfo profilerInfo = null;
- if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
- if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
- mService.mAm.mProfileProc = app;
- ProfilerInfo profilerInfoSvc = mService.mAm.mProfilerInfo;
- if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
- if (profilerInfoSvc.profileFd != null) {
- try {
- profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
- } catch (IOException e) {
- profilerInfoSvc.closeFd();
- }
- }
+ ProfilerInfo profilerInfo = proc.onStartActivity(mService.mTopProcessState);
- profilerInfo = new ProfilerInfo(profilerInfoSvc);
- }
- }
- }
-
- app.hasShownUi = true;
- app.pendingUiClean = true;
- app.forceProcessStateUpTo(mService.mTopProcessState);
// Because we could be starting an Activity in the system process this may not go
// across a Binder interface which would create a new Configuration. Consequently
// we have to always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- app.getWindowProcessController().getConfiguration(),
- r.getMergedOverrideConfiguration());
+ proc.getConfiguration(), r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
// Create activity launch transaction.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
- r.appToken);
+ final ClientTransaction clientTransaction = ClientTransaction.obtain(
+ proc.getThread(), r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
- r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+ r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.icicle, r.persistentState, results, newIntents,
mService.isNextTransitionForward(), profilerInfo));
@@ -1526,12 +1446,12 @@
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
- if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
+ if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mHasHeavyWeightFeature) {
// This may be a heavy-weight process! Note that the package manager will ensure
// that only activity can run in the main process of the .apk, which is the only
// thing that will be considered heavy-weight.
- if (app.processName.equals(app.info.packageName)) {
+ if (proc.mName.equals(proc.mInfo.packageName)) {
if (mService.mHeavyWeightProcess != null
&& mService.mHeavyWeightProcess != proc) {
Slog.w(TAG, "Starting new heavy weight process " + proc
@@ -1544,12 +1464,10 @@
} catch (RemoteException e) {
if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
+ // This is the second time we failed -- finish activity and give up.
Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- mService.mAm.appDiedLocked(app);
+ + r.intent.getComponent().flattenToShortString() + ", giving up", e);
+ proc.appDied();
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"2nd-crash", false);
return false;
@@ -1658,24 +1576,21 @@
}
}
- void startSpecificActivityLocked(ActivityRecord r,
- boolean andResume, boolean checkConfig) {
+ void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
- ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid, true);
+ final WindowProcessController wpc =
+ mService.getProcessController(r.processName, r.info.applicationInfo.uid);
- if (app != null && app.thread != null) {
+ if (wpc != null && wpc.hasThread()) {
try {
- if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
+ if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
- // Don't add this if it is a platform component that is marked
- // to run in multiple processes, because this is actually
- // part of the framework so doesn't make sense to track as a
- // separate apk in the process.
- app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
- mService.mAm.mProcessStats);
+ // Don't add this if it is a platform component that is marked to run in
+ // multiple processes, because this is actually part of the framework so doesn't
+ // make sense to track as a separate apk in the process.
+ wpc.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode);
}
- realStartActivityLocked(r, app, andResume, checkConfig);
+ realStartActivityLocked(r, wpc, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
@@ -1686,8 +1601,12 @@
// restart the application.
}
- mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false, false, true);
+ // Post message to start process to avoid possible deadlock of calling into AMS with the
+ // ATMS lock held.
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
+ r.info.applicationInfo, true, "activity", r.intent.getComponent());
+ mService.mH.sendMessage(msg);
}
void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
@@ -1717,24 +1636,25 @@
sendHint = noResumedActivities || allFocusedProcessesDiffer;
}
- if (sendHint && mService.mAm.mLocalPowerManager != null) {
- mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
+ if (sendHint && mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
mPowerHintSent = true;
}
}
void sendPowerHintForLaunchEndIfNeeded() {
// Trigger launch power hint if activity is launched
- if (mPowerHintSent && mService.mAm.mLocalPowerManager != null) {
- mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
+ if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
mPowerHintSent = false;
}
}
- boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
- String resultWho, int requestCode, int callingPid, int callingUid,
- String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask,
- ProcessRecord callerApp, ActivityRecord resultRecord, ActivityStack resultStack) {
+ boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
+ int requestCode, int callingPid, int callingUid, String callingPackage,
+ boolean ignoreTargetSecurity, boolean launchingInTask,
+ WindowProcessController callerApp, ActivityRecord resultRecord,
+ ActivityStack resultStack) {
final boolean isCallerRecents = mService.getRecentTasks() != null
&& mService.getRecentTasks().isCallerRecents(callingUid);
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
@@ -2257,9 +2177,9 @@
* Finish the topmost activities in all stacks that belong to the crashed app.
* @param app The app that crashed.
* @param reason Reason to perform this action.
- * @return The task that was finished in this stack, {@code null} if haven't found any.
+ * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
*/
- TaskRecord finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
+ int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
TaskRecord finishedTask = null;
ActivityStack focusedStack = getTopDisplayFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -2274,7 +2194,7 @@
}
}
}
- return finishedTask;
+ return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
}
void finishVoiceTask(IVoiceInteractionSession session) {
@@ -2304,17 +2224,8 @@
mUserLeaving = true;
}
- // TODO(b/111363427): The moving-to-top task may not be on the top display, so it could be
- // different from where the prev activity stays on.
- final ActivityRecord prev = topRunningActivityLocked();
-
- if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0
- || (prev != null && prev.isActivityTypeRecents())) {
- // Caller wants the home activity moved with it or the previous task is recents in which
- // case we always return home from the task we are moving to the front.
- currentStack.getDisplay().moveHomeStackToFront("findTaskToMoveToFront");
- }
-
+ reason = reason + " findTaskToMoveToFront";
+ boolean reparented = false;
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
final Rect bounds = options.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
@@ -2322,10 +2233,12 @@
ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
if (stack != currentStack) {
+ moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
- "findTaskToMoveToFront");
+ reason);
currentStack = stack;
- // moveTaskToStackUncheckedLocked() should already placed the task on top,
+ reparented = true;
+ // task.reparent() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (stack.resizeStackWithLaunchBounds()) {
@@ -2340,6 +2253,10 @@
}
}
+ if (!reparented) {
+ moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplay(), reason);
+ }
+
final ActivityRecord r = task.getTopActivity();
currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
@@ -2351,6 +2268,18 @@
currentStack, forceNonResizeable);
}
+ private void moveHomeStackToFrontIfNeeded(int flags, ActivityDisplay display, String reason) {
+ final ActivityStack focusedStack = display.getFocusedStack();
+
+ if ((display.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
+ || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
+ // We move home stack to front when we are on a fullscreen display and caller has
+ // requested the home activity to move with it. Or the previous stack is recents.
+ display.moveHomeStackToFront(reason);
+ }
+ }
+
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
// We use the launch bounds in the activity options is the device supports freeform
// window management or is launching into the pinned stack.
@@ -2456,7 +2385,7 @@
}
if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
if (r != null) {
- stack = (T) getValidLaunchStackOnDisplay(displayId, r, options);
+ stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options);
if (stack != null) {
return stack;
}
@@ -2521,10 +2450,11 @@
* If there is no such stack, new dynamic stack can be created.
* @param displayId Target display.
* @param r Activity that should be launched there.
+ * @param candidateTask The possible task the activity might be put in.
* @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
*/
ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
- @Nullable ActivityOptions options) {
+ @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options) {
final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
if (activityDisplay == null) {
throw new IllegalArgumentException(
@@ -2535,6 +2465,13 @@
return null;
}
+ // If {@code r} is already in target display and its task is the same as the candidate task,
+ // the intention should be getting a launch stack for the reusable activity, so we can use
+ // the existing stack.
+ if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
+ return candidateTask.getStack();
+ }
+
// Return the topmost valid stack on the display.
for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
final ActivityStack stack = activityDisplay.getChildAt(i);
@@ -2555,6 +2492,11 @@
return null;
}
+ ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+ @Nullable ActivityOptions options) {
+ return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options);
+ }
+
// TODO: Can probably be consolidated into getLaunchStack()...
private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
switch (stack.getActivityType()) {
@@ -3072,7 +3014,9 @@
}
// Find any running services associated with this app and stop if needed.
- mService.mAm.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+ final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
+ mService.mAmInternal, tr.userId, component, new Intent(tr.getBaseIntent()));
+ mService.mH.sendMessage(msg);
if (!killProcess) {
return;
@@ -3422,7 +3366,7 @@
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivity.release();
- mService.mAm.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+ mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index f6f1e55..20d5ab2 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -22,8 +22,8 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -65,7 +65,7 @@
* through the pending activity list, and recording home activity launches.
*/
public class ActivityStartController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_ATM;
private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
@@ -91,6 +91,8 @@
private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
+ boolean mCheckedForSetup = false;
+
private final class StartHandler extends Handler {
public StartHandler(Looper looper) {
super(looper, null, true);
@@ -193,7 +195,7 @@
*/
void startSetupActivity() {
// Only do this once per boot.
- if (mService.mAm.getCheckedForSetup()) {
+ if (mCheckedForSetup) {
return;
}
@@ -203,7 +205,7 @@
final ContentResolver resolver = mService.mContext.getContentResolver();
if (mService.mFactoryTest != FACTORY_TEST_LOW_LEVEL
&& Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
- mService.mAm.setCheckedForSetup(true);
+ mCheckedForSetup = true;
// See if we should be showing the platform update setup UI.
final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
@@ -357,7 +359,7 @@
null, userId, ActivityStarter.computeResolveFilterUid(
callingUid, realCallingUid, UserHandle.USER_NULL));
// TODO: New, check if this is correct
- aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
+ aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
if (aInfo != null &&
(aInfo.applicationInfo.privateFlags
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 4789ff3..e51824f 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -44,6 +44,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -246,9 +247,9 @@
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
return interceptSuspendedByAdminPackage();
}
- final String dialogMessage = pmi.getSuspendedDialogMessage(suspendedPackage, mUserId);
+ final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId);
mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
- suspendingPackage, dialogMessage, mUserId);
+ suspendingPackage, dialogInfo, mUserId);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 33f949f..45a0652 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -53,25 +53,25 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
@@ -127,7 +127,7 @@
* an activity and associated task and stack.
*/
class ActivityStarter {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_ATM;
private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
@@ -590,12 +590,12 @@
final Bundle verificationBundle
= options != null ? options.popAppVerificationBundle() : null;
- ProcessRecord callerApp = null;
+ WindowProcessController callerApp = null;
if (caller != null) {
- callerApp = mService.mAm.getRecordForAppLocked(caller);
+ callerApp = mService.getProcessController(caller);
if (callerApp != null) {
- callingPid = callerApp.pid;
- callingUid = callerApp.info.uid;
+ callingPid = callerApp.getPid();
+ callingUid = callerApp.mInfo.uid;
} else {
Slog.w(TAG, "Unable to find app for caller " + caller
+ " (pid=" + callingPid + ") when starting: "
@@ -726,14 +726,12 @@
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
inTask != null, callerApp, resultRecord, resultStack);
- abort |= !mService.mAm.mIntentFirewall.checkStartActivity(intent, callingUid,
+ abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
- final WindowProcessController callerWpc =
- callerApp != null ? callerApp.getWindowProcessController() : null;
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
- ? options.getOptions(intent, aInfo, callerWpc, mSupervisor) : null;
+ ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
if (allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
@@ -833,8 +831,7 @@
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
}
- ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid,
- callingUid,
+ ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, checkedOptions, sourceRecord);
@@ -857,7 +854,7 @@
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "Activity start")) {
mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, stack, callerWpc));
+ sourceRecord, startFlags, stack, callerApp));
ActivityOptions.abort(checkedOptions);
return ActivityManager.START_SWITCHES_CANCELED;
}
@@ -874,12 +871,11 @@
}
private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
- Intent intent, ProcessRecord callerApp, ActivityRecord r,
+ Intent intent, WindowProcessController callerApp, ActivityRecord r,
PendingIntentRecord originatingPendingIntent) {
- boolean callerAppHasForegroundActivity = (callerApp != null)
- ? callerApp.foregroundActivities
- : false;
- if (!mService.mAm.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
+ boolean callerAppHasForegroundActivity =
+ callerApp != null && callerApp.hasForegroundActivities();
+ if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
|| r == null) {
// skip logging in this case
return;
@@ -1085,9 +1081,10 @@
|| !heavy.mName.equals(aInfo.processName))) {
int appCallingUid = callingUid;
if (caller != null) {
- ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller);
+ WindowProcessController callerApp =
+ mService.getProcessController(caller);
if (callerApp != null) {
- appCallingUid = callerApp.info.uid;
+ appCallingUid = callerApp.mInfo.uid;
} else {
Slog.w(TAG, "Unable to find app for caller " + caller
+ " (pid=" + callingPid + ") when starting: "
@@ -1127,7 +1124,7 @@
callingUid, realCallingUid, mRequest.filterCallingUid));
aInfo = rInfo != null ? rInfo.activityInfo : null;
if (aInfo != null) {
- aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
+ aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
}
}
}
@@ -1280,6 +1277,7 @@
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
+ final int preferredWindowingMode = mLaunchParams.mWindowingMode;
// Do not start home activity if it cannot be launched on preferred display. We are not
// doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
@@ -1298,25 +1296,6 @@
ActivityRecord reusedActivity = getReusableIntentActivity();
- int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;
- int preferredLaunchDisplayId = DEFAULT_DISPLAY;
- if (mOptions != null) {
- preferredWindowingMode = mOptions.getLaunchWindowingMode();
- preferredLaunchDisplayId = mOptions.getLaunchDisplayId();
- }
-
- // windowing mode and preferred launch display values from {@link LaunchParams} take
- // priority over those specified in {@link ActivityOptions}.
- if (!mLaunchParams.isEmpty()) {
- if (mLaunchParams.hasPreferredDisplay()) {
- preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;
- }
-
- if (mLaunchParams.hasWindowingMode()) {
- preferredWindowingMode = mLaunchParams.mWindowingMode;
- }
- }
-
if (reusedActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
@@ -1463,7 +1442,7 @@
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
- preferredLaunchDisplayId, topStack);
+ mPreferredDisplayId, topStack);
return START_DELIVERED_TO_TOP;
}
@@ -1493,8 +1472,9 @@
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
- mService.mAm.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
- UserHandle.getAppId(mStartActivity.appInfo.uid), UserHandle.getAppId(mCallingUid));
+ mService.getPackageManagerInternalLocked().grantEphemeralAccess(
+ mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
+ UserHandle.getAppId(mCallingUid));
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
mStartActivity.getTask().taskId);
@@ -1542,7 +1522,7 @@
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
- preferredLaunchDisplayId, mTargetStack);
+ mPreferredDisplayId, mTargetStack);
return START_SUCCESS;
}
@@ -1608,15 +1588,14 @@
mLaunchParams.reset();
- mSupervisor.getLaunchParamsController().calculate(inTask, null /*layout*/, r, sourceRecord,
- options, mLaunchParams);
+ mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
+ sourceRecord, options, mLaunchParams);
if (mLaunchParams.hasPreferredDisplay()) {
mPreferredDisplayId = mLaunchParams.mPreferredDisplayId;
} else {
mPreferredDisplayId = DEFAULT_DISPLAY;
}
- ensureValidPreferredDisplayId(r);
mLaunchMode = r.launchMode;
@@ -1709,24 +1688,6 @@
mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
}
- /**
- * Ensure preferred display ID matches the starting activity.
- */
- private void ensureValidPreferredDisplayId(ActivityRecord startingActivity) {
- // Check if the Activity is a VR activity. If so, the activity should be launched in
- // main display.
- if (startingActivity != null && startingActivity.requestedVrComponent != null) {
- mPreferredDisplayId = DEFAULT_DISPLAY;
- }
-
- // Get the virtual display ID from ActivityStackManagerService. If that's set we should
- // always use that.
- final int displayId = mService.mVr2dDisplayId;
- if (displayId != INVALID_DISPLAY) {
- mPreferredDisplayId = displayId;
- }
- }
-
private void sendNewTaskResultRequestIfNeeded() {
final ActivityStack sourceStack = mStartActivity.resultTo != null
? mStartActivity.resultTo.getStack() : null;
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java
new file mode 100644
index 0000000..cf72738
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+/**
+ * Common class for the various debug {@link android.util.Log} output configuration relating to
+ * activities.
+ */
+public class ActivityTaskManagerDebugConfig {
+ // All output logs relating to acitvities use the {@link #TAG_ATM} string for tagging their log
+ // output. This makes it easy to identify the origin of the log message when sifting
+ // through a large amount of log output from multiple sources. However, it also makes trying
+ // to figure-out the origin of a log message while debugging the activity manager a little
+ // painful. By setting this constant to true, log messages from the activity manager package
+ // will be tagged with their class names instead fot the generic tag.
+ static final boolean TAG_WITH_CLASS_NAME = false;
+
+ // While debugging it is sometimes useful to have the category name of the log appended to the
+ // base log tag to make sifting through logs with the same base tag easier. By setting this
+ // constant to true, the category name of the log point will be appended to the log tag.
+ private static final boolean APPEND_CATEGORY_NAME = false;
+
+ // Default log tag for the activities.
+ static final String TAG_ATM = "ActivityTaskManager";
+
+ // Enable all debug log categories.
+ static final boolean DEBUG_ALL = false;
+
+ // Enable all debug log categories for activities.
+ private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
+
+ static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
+ static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_FOCUS = false;
+ static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
+ static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
+ static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
+ static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
+ static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
+ static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_STACK = DEBUG_ALL || false;
+ static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
+ static final boolean DEBUG_TASKS = DEBUG_ALL || false;
+ static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
+ static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
+ static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
+ static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
+ static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
+ static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;
+ static final boolean DEBUG_METRICS = DEBUG_ALL || false;
+
+ static final String POSTFIX_APP = APPEND_CATEGORY_NAME ? "_App" : "";
+ static final String POSTFIX_IDLE = APPEND_CATEGORY_NAME ? "_Idle" : "";
+ static final String POSTFIX_RELEASE = APPEND_CATEGORY_NAME ? "_Release" : "";
+ static final String POSTFIX_USER_LEAVING = APPEND_CATEGORY_NAME ? "_UserLeaving" : "";
+ static final String POSTFIX_ADD_REMOVE = APPEND_CATEGORY_NAME ? "_AddRemove" : "";
+ static final String POSTFIX_CONFIGURATION = APPEND_CATEGORY_NAME ? "_Configuration" : "";
+ static final String POSTFIX_CONTAINERS = APPEND_CATEGORY_NAME ? "_Containers" : "";
+ static final String POSTFIX_FOCUS = APPEND_CATEGORY_NAME ? "_Focus" : "";
+ static final String POSTFIX_IMMERSIVE = APPEND_CATEGORY_NAME ? "_Immersive" : "";
+ static final String POSTFIX_LOCKTASK = APPEND_CATEGORY_NAME ? "_LockTask" : "";
+ static final String POSTFIX_PAUSE = APPEND_CATEGORY_NAME ? "_Pause" : "";
+ static final String POSTFIX_RECENTS = APPEND_CATEGORY_NAME ? "_Recents" : "";
+ static final String POSTFIX_SAVED_STATE = APPEND_CATEGORY_NAME ? "_SavedState" : "";
+ static final String POSTFIX_STACK = APPEND_CATEGORY_NAME ? "_Stack" : "";
+ static final String POSTFIX_STATES = APPEND_CATEGORY_NAME ? "_States" : "";
+ static final String POSTFIX_SWITCH = APPEND_CATEGORY_NAME ? "_Switch" : "";
+ static final String POSTFIX_TASKS = APPEND_CATEGORY_NAME ? "_Tasks" : "";
+ static final String POSTFIX_TRANSITION = APPEND_CATEGORY_NAME ? "_Transition" : "";
+ static final String POSTFIX_VISIBILITY = APPEND_CATEGORY_NAME ? "_Visibility" : "";
+ static final String POSTFIX_RESULTS = APPEND_CATEGORY_NAME ? "_Results" : "";
+}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index b369b71..f79d9aa 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -38,8 +38,11 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
+import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
@@ -50,6 +53,7 @@
import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
+import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
@@ -67,29 +71,41 @@
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
-import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG;
import static com.android.server.am.ActivityManagerService.dumpStackTraces;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
@@ -100,7 +116,7 @@
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -141,7 +157,7 @@
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
-import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -179,6 +195,7 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -189,6 +206,8 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
@@ -219,9 +238,12 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.TransferPipe;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AppOpsService;
@@ -230,6 +252,7 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.vr.VrManagerInternal;
@@ -237,16 +260,23 @@
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
+import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
/**
* System service for managing activities and their containers (task, stacks, displays,... ).
@@ -254,7 +284,7 @@
* {@hide}
*/
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
@@ -264,16 +294,40 @@
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
// How long we wait until we timeout on key dispatching.
- private static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
+ public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
// How long we wait until we timeout on key dispatching during instrumentation.
- private static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+ static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+
+ /** Used to indicate that an app transition should be animated. */
+ static final boolean ANIMATE = true;
+
+ /** Hardware-reported OpenGLES version. */
+ final int GL_ES_VERSION;
+
+ public static final String DUMP_ACTIVITIES_CMD = "activities" ;
+ public static final String DUMP_ACTIVITIES_SHORT_CMD = "a" ;
+ public static final String DUMP_LASTANR_CMD = "lastanr" ;
+ public static final String DUMP_LASTANR_TRACES_CMD = "lastanr-traces" ;
+ public static final String DUMP_STARTER_CMD = "starter" ;
+ public static final String DUMP_CONTAINERS_CMD = "containers" ;
+ public static final String DUMP_RECENTS_CMD = "recents" ;
+ public static final String DUMP_RECENTS_SHORT_CMD = "r" ;
+
+ /** This activity is not being relaunched, or being relaunched for a non-resize reason. */
+ public static final int RELAUNCH_REASON_NONE = 0;
+ /** This activity is being relaunched due to windowing mode change. */
+ public static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
+ /** This activity is being relaunched due to a free-resize operation. */
+ public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
Context mContext;
+
/**
* This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
* change at runtime. Use mContext for non-UI purposes.
*/
final Context mUiContext;
+ final ActivityThread mSystemThread;
H mH;
UiHandler mUiHandler;
ActivityManagerService mAm;
@@ -281,7 +335,12 @@
UriGrantsManagerInternal mUgmInternal;
private PackageManagerInternal mPmInternal;
private ActivityTaskManagerInternal mInternal;
+ PowerManagerInternal mPowerManagerInternal;
+ private UsageStatsManagerInternal mUsageStatsInternal;
+
PendingIntentController mPendingIntentController;
+ IntentFirewall mIntentFirewall;
+
/* Global service lock used by the package the owns this service. */
Object mGlobalLock;
ActivityStackSupervisor mStackSupervisor;
@@ -400,6 +459,12 @@
String mTopData;
/**
+ * Dump of the activity state at the time of the last ANR. Cleared after
+ * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
+ */
+ String mLastANRState;
+
+ /**
* Used to retain an update lock when the foreground activity is in
* immersive mode.
*/
@@ -536,8 +601,10 @@
ActivityTaskManagerService(Context context) {
mContext = context;
mFactoryTest = FactoryTest.getMode();
- mUiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ mSystemThread = ActivityThread.currentActivityThread();
+ mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
+ GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
}
void onSystemReady() {
@@ -551,6 +618,7 @@
void onInitPowerManagement() {
mStackSupervisor.initPowerManagement();
final PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
mVoiceWakeLock.setReferenceCounted(false);
}
@@ -637,15 +705,17 @@
}
// TODO: Will be converted to WM lock once transition is complete.
- void setActivityManagerService(ActivityManagerService am) {
+ void setActivityManagerService(ActivityManagerService am, Looper looper,
+ IntentFirewall intentFirewall, PendingIntentController intentController) {
mAm = am;
mGlobalLock = mAm;
- mH = new H(mAm.mHandlerThread.getLooper());
+ mH = new H(looper);
mUiHandler = new UiHandler();
+ mIntentFirewall = intentFirewall;
final File systemDir = SystemServiceManager.ensureSystemDir();
mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
mCompatModePackages = new CompatModePackages(this, systemDir, mH);
- mPendingIntentController = mAm.mPendingIntentController;
+ mPendingIntentController = intentController;
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
@@ -682,6 +752,11 @@
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
mLockTaskController.setWindowManager(wm);
+ mStackSupervisor.setWindowManager(wm);
+ }
+
+ void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
+ mUsageStatsInternal = usageStatsManager;
}
UserManagerService getUserManager() {
@@ -763,7 +838,7 @@
&& globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
}
- config.reqGlEsVersion = mAm.GL_ES_VERSION;
+ config.reqGlEsVersion = GL_ES_VERSION;
}
return config;
}
@@ -1294,7 +1369,7 @@
Slog.i(TAG, "Removing task failed to finish activity");
}
// Explicitly dismissing the activity so reset its relaunch flag.
- r.mRelaunchReason = ActivityRecord.RELAUNCH_REASON_NONE;
+ r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
@@ -2594,7 +2669,7 @@
pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mAmInternal.closeSystemDialogs("assist");
+ mInternal.closeSystemDialogs("assist");
try {
mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
@@ -2761,8 +2836,7 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- WindowProcessController app =
- mAm.getRecordForAppLocked(appInt).getWindowProcessController();
+ final WindowProcessController app = getProcessController(appInt);
mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2783,7 +2857,10 @@
long ident = Binder.clearCallingIdentity();
if (mKeyguardShown != keyguardShowing) {
mKeyguardShown = keyguardShowing;
- reportCurKeyguardUsageEventLocked(keyguardShowing);
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
+ keyguardShowing);
+ mH.sendMessage(msg);
}
try {
mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
@@ -2920,12 +2997,6 @@
mTaskChangeNotificationController.unregisterTaskStackListener(listener);
}
- private void reportCurKeyguardUsageEventLocked(boolean keyguardShowing) {
- mAm.reportGlobalUsageEventLocked(keyguardShowing
- ? UsageEvents.Event.KEYGUARD_SHOWN
- : UsageEvents.Event.KEYGUARD_HIDDEN);
- }
-
@Override
public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
@@ -3892,8 +3963,9 @@
}
if (mWindowManager != null) {
- // Update OOM levels based on display size.
- mAm.mProcessList.applyDisplaySize(mWindowManager);
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal, displayId);
+ mH.sendMessage(msg);
}
final long origId = Binder.clearCallingIdentity();
@@ -3921,8 +3993,10 @@
}
if (mWindowManager != null) {
- // Update OOM levels based on display size.
- mAm.mProcessList.applyDisplaySize(mWindowManager);
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,
+ DEFAULT_DISPLAY);
+ mH.sendMessage(msg);
}
final long origId = Binder.clearCallingIdentity();
@@ -4129,11 +4203,9 @@
public void setVrThread(int tid) {
enforceSystemHasVrFeature();
synchronized (mGlobalLock) {
- synchronized (mAm.mPidsSelfLocked) {
- final int pid = Binder.getCallingPid();
- final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
- mVrController.setVrThreadLocked(tid, pid, proc.getWindowProcessController());
- }
+ final int pid = Binder.getCallingPid();
+ final WindowProcessController wpc = mPidMap.get(pid);
+ mVrController.setVrThreadLocked(tid, pid, wpc);
}
}
@@ -4150,11 +4222,9 @@
}
enforceSystemHasVrFeature();
synchronized (mGlobalLock) {
- synchronized (mAm.mPidsSelfLocked) {
- final int pid = Binder.getCallingPid();
- final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
- mVrController.setPersistentVrThreadLocked(tid, pid, proc);
- }
+ final int pid = Binder.getCallingPid();
+ final WindowProcessController proc = mPidMap.get(pid);
+ mVrController.setPersistentVrThreadLocked(tid, pid, proc);
}
}
@@ -4262,6 +4332,17 @@
}
}
+ public static String relaunchReasonToString(int relaunchReason) {
+ switch (relaunchReason) {
+ case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
+ return "window_resize";
+ case RELAUNCH_REASON_FREE_RESIZE:
+ return "free_resize";
+ default:
+ return null;
+ }
+ }
+
ActivityStack getTopDisplayFocusedStack() {
return mStackSupervisor.getTopDisplayFocusedStack();
}
@@ -4286,16 +4367,177 @@
|| transit == TRANSIT_TASK_TO_FRONT;
}
- void dumpSleepStates(PrintWriter pw, boolean testPssMode) {
- synchronized (mGlobalLock) {
- pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
- if (mRunningVoice != null) {
- pw.println(" mRunningVoice=" + mRunningVoice);
- pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
+ void dumpLastANRLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
+ if (mLastANRState == null) {
+ pw.println(" <no ANR has occurred since boot>");
+ } else {
+ pw.println(mLastANRState);
+ }
+ }
+
+ void dumpLastANRTracesLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
+
+ final File[] files = new File(ANR_TRACE_DIR).listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ pw.println(" <no ANR has occurred since boot>");
+ return;
+ }
+ // Find the latest file.
+ File latest = null;
+ for (File f : files) {
+ if ((latest == null) || (latest.lastModified() < f.lastModified())) {
+ latest = f;
}
- pw.println(" mSleeping=" + mSleeping);
- pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
- pw.println(" mVrController=" + mVrController);
+ }
+ pw.print("File: ");
+ pw.print(latest.getName());
+ pw.println();
+ try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
+ String line;
+ while ((line = in.readLine()) != null) {
+ pw.println(line);
+ }
+ } catch (IOException e) {
+ pw.print("Unable to read: ");
+ pw.print(e);
+ pw.println();
+ }
+ }
+
+ void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
+ dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
+ "ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
+ }
+
+ void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
+ pw.println(header);
+
+ boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+ dumpPackage);
+ boolean needSep = printedAnything;
+
+ boolean printed = ActivityStackSupervisor.printThisActivity(pw,
+ mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity: ");
+ if (printed) {
+ printedAnything = true;
+ needSep = false;
+ }
+
+ if (dumpPackage == null) {
+ if (needSep) {
+ pw.println();
+ }
+ printedAnything = true;
+ mStackSupervisor.dump(pw, " ");
+ }
+
+ if (!printedAnything) {
+ pw.println(" (nothing)");
+ }
+ }
+
+ void dumpActivityContainersLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
+ mStackSupervisor.dumpChildrenNames(pw, " ");
+ pw.println(" ");
+ }
+
+ void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
+ getActivityStartController().dump(pw, "", dumpPackage);
+ }
+
+ /**
+ * There are three things that cmd can be:
+ * - a flattened component name that matches an existing activity
+ * - the cmd arg isn't the flattened component name of an existing activity:
+ * dump all activity whose component contains the cmd as a substring
+ * - A hex number of the ActivityRecord object instance.
+ *
+ * @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
+ * @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
+ */
+ protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
+ ArrayList<ActivityRecord> activities;
+
+ synchronized (mGlobalLock) {
+ activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+ dumpFocusedStackOnly);
+ }
+
+ if (activities.size() <= 0) {
+ return false;
+ }
+
+ String[] newArgs = new String[args.length - opti];
+ System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
+ TaskRecord lastTask = null;
+ boolean needSep = false;
+ for (int i = activities.size() - 1; i >= 0; i--) {
+ ActivityRecord r = activities.get(i);
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ synchronized (mGlobalLock) {
+ final TaskRecord task = r.getTask();
+ if (lastTask != task) {
+ lastTask = task;
+ pw.print("TASK "); pw.print(lastTask.affinity);
+ pw.print(" id="); pw.print(lastTask.taskId);
+ pw.print(" userId="); pw.println(lastTask.userId);
+ if (dumpAll) {
+ lastTask.dump(pw, " ");
+ }
+ }
+ }
+ dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
+ }
+ return true;
+ }
+
+ /**
+ * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
+ * there is a thread associated with the activity.
+ */
+ private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
+ final ActivityRecord r, String[] args, boolean dumpAll) {
+ String innerPrefix = prefix + " ";
+ synchronized (mGlobalLock) {
+ pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
+ pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
+ pw.print(" pid=");
+ if (r.hasProcess()) pw.println(r.app.getPid());
+ else pw.println("(not running)");
+ if (dumpAll) {
+ r.dump(pw, innerPrefix);
+ }
+ }
+ if (r.attachedToProcess()) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.getThread().dumpActivity(tp.getWriteFd(),
+ r.appToken, innerPrefix, args);
+ tp.go(fd);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
}
}
@@ -4428,14 +4670,6 @@
return kept;
}
- /**
- * Returns true if this configuration change is interesting enough to send an
- * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
- */
- private static boolean isSplitConfigurationChange(int configDiff) {
- return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
- }
-
/** Update default (global) configuration and notify listeners about changes. */
private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId, boolean deferResume) {
@@ -4486,8 +4720,11 @@
SystemProperties.set("persist.sys.locale",
locales.get(bestLocaleIndex).toLanguageTag());
LocaleList.setDefault(locales, bestLocaleIndex);
- mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
- locales.get(bestLocaleIndex)));
+
+ final Message m = PooledLambda.obtainMessage(
+ ActivityTaskManagerService::sendLocaleToMountDaemonMsg, this,
+ locales.get(bestLocaleIndex));
+ mH.sendMessage(m);
}
mTempConfig.seq = increaseConfigurationSeqLocked();
@@ -4497,8 +4734,7 @@
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// TODO(multi-display): Update UsageEvents#Event to include displayId.
- mAm.mUsageStatsService.reportConfigurationChange(
- mTempConfig, mAmInternal.getCurrentUserId());
+ mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());
// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
updateShouldShowDialogsLocked(mTempConfig);
@@ -4512,16 +4748,16 @@
// to retrieve resource values after we return will be sure to get the new ones. This is
// especially important during boot, where the first config change needs to guarantee all
// resources have that config before following boot code is executed.
- mAm.mSystemThread.applyConfigurationToResources(mTempConfig);
+ mSystemThread.applyConfigurationToResources(mTempConfig);
// We need another copy of global config because we're scheduling some calls instead of
// running them in place. We need to be sure that object we send will be handled unchanged.
final Configuration configCopy = new Configuration(mTempConfig);
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
- Message msg = mAm.mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
- msg.obj = configCopy;
- msg.arg1 = userId;
- mAm.mHandler.sendMessage(msg);
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityTaskManagerService::sendPutConfigurationForUserMsg,
+ this, userId, configCopy);
+ mH.sendMessage(msg);
}
for (int i = mPidMap.size() - 1; i >= 0; i--) {
@@ -4533,38 +4769,10 @@
app.onConfigurationChanged(configCopy);
}
- Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- UserHandle.USER_ALL);
- if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
- intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- if (initLocale || !mAm.mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- }
- mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- UserHandle.USER_ALL);
- }
-
- // Send a broadcast to PackageInstallers if the configuration change is interesting
- // for the purposes of installing additional splits.
- if (!initLocale && isSplitConfigurationChange(changes)) {
- intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-
- // Typically only app stores will have this permission.
- String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
- mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- }
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::broadcastGlobalConfigurationChanged,
+ mAmInternal, changes, initLocale);
+ mH.sendMessage(msg);
// Override configuration of the default display duplicates global config, so we need to
// update it also. This will also notify WindowManager about changes.
@@ -4633,8 +4841,12 @@
if (isDensityChange && displayId == DEFAULT_DISPLAY) {
mAppWarnings.onDensityChanged();
- mAm.killAllBackgroundProcessesExcept(N,
- ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ // Post message to start process to avoid possible deadlock of calling into AMS with
+ // the ATMS lock held.
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::killAllBackgroundProcessesExcept, mAmInternal,
+ N, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ mH.sendMessage(msg);
}
}
@@ -4659,6 +4871,26 @@
mWindowManager.setEventDispatching(booted && !mShuttingDown);
}
+ private void sendPutConfigurationForUserMsg(int userId, Configuration config) {
+ final ContentResolver resolver = mContext.getContentResolver();
+ Settings.System.putConfigurationForUser(resolver, config, userId);
+ }
+
+ private void sendLocaleToMountDaemonMsg(Locale l) {
+ try {
+ IBinder service = ServiceManager.getService("mount");
+ IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
+ Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
+ storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error storing locale for decryption UI", e);
+ }
+ }
+
+ boolean isActivityStartsLoggingEnabled() {
+ return mAmInternal.isActivityStartsLoggingEnabled();
+ }
+
void enableScreenAfterBoot(boolean booted) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
@@ -4686,70 +4918,7 @@
}
private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
- if (r != null && (r.isInstrumenting() || r.isUsingWrapper())) {
- return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
- }
- return KEY_DISPATCHING_TIMEOUT_MS;
- }
-
- long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
- if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission " + FILTER_EVENTS);
- }
- WindowProcessController proc;
- long timeout;
- synchronized (mGlobalLock) {
- proc = mPidMap.get(pid);
- timeout = getInputDispatchingTimeoutLocked(proc);
- }
-
- if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
- return -1;
- }
-
- return timeout;
- }
-
- /**
- * Handle input dispatching timeouts.
- * Returns whether input dispatching should be aborted or not.
- */
- boolean inputDispatchingTimedOut(final WindowProcessController proc,
- final ActivityRecord activity, final ActivityRecord parent,
- final boolean aboveSystem, String reason) {
- if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission " + FILTER_EVENTS);
- }
-
- final String annotation;
- if (reason == null) {
- annotation = "Input dispatching timed out";
- } else {
- annotation = "Input dispatching timed out (" + reason + ")";
- }
-
- if (proc != null) {
- synchronized (mGlobalLock) {
- if (proc.isDebugging()) {
- return false;
- }
-
- if (proc.isInstrumenting()) {
- Bundle info = new Bundle();
- info.putString("shortMsg", "keyDispatchingTimedOut");
- info.putString("longMsg", annotation);
- mAm.finishInstrumentationLocked(
- (ProcessRecord) proc.mOwner, Activity.RESULT_CANCELED, info);
- return true;
- }
- }
- mH.post(() -> {
- mAm.mAppErrors.appNotResponding(
- (ProcessRecord) proc.mOwner, activity, parent, aboveSystem, annotation);
- });
- }
-
- return true;
+ return r != null ? r.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
}
/**
@@ -5245,7 +5414,7 @@
return newInfo;
}
- private WindowProcessController getProcessController(String processName, int uid) {
+ WindowProcessController getProcessController(String processName, int uid) {
if (uid == SYSTEM_UID) {
// The system gets to run in any process. If there are multiple processes with the same
// uid, just pick the first (this should never happen).
@@ -5266,6 +5435,26 @@
return mProcessNames.get(processName, uid);
}
+ WindowProcessController getProcessController(IApplicationThread thread) {
+ if (thread == null) {
+ return null;
+ }
+
+ final IBinder threadBinder = thread.asBinder();
+ final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap();
+ for (int i = pmap.size()-1; i >= 0; i--) {
+ final SparseArray<WindowProcessController> procs = pmap.valueAt(i);
+ for (int j = procs.size() - 1; j >= 0; j--) {
+ final WindowProcessController proc = procs.valueAt(j);
+ if (proc.hasThread() && proc.getThread().asBinder() == threadBinder) {
+ return proc;
+ }
+ }
+ }
+
+ return null;
+ }
+
void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
if (true || Build.IS_USER) {
return;
@@ -5327,6 +5516,8 @@
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
+ static final int FIRST_ACTIVITY_STACK_MSG = 100;
+ static final int FIRST_SUPERVISOR_STACK_MSG = 200;
public H(Looper looper) {
super(looper, null, true);
@@ -5673,6 +5864,9 @@
@Override
public void finishHeavyWeightApp() {
synchronized (mGlobalLock) {
+ if (mHeavyWeightProcess != null) {
+ mHeavyWeightProcess.finishActivities();
+ }
ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
mHeavyWeightProcess);
}
@@ -5763,14 +5957,6 @@
}
@Override
- public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
- synchronized (mGlobalLock) {
- return ActivityTaskManagerService.this.inputDispatchingTimedOut(
- pid, aboveSystem, reason);
- }
- }
-
- @Override
public void onProcessMapped(int pid, WindowProcessController proc) {
synchronized (mGlobalLock) {
mPidMap.put(pid, proc);
@@ -5808,6 +5994,13 @@
}
@Override
+ public void onPackageReplaced(ApplicationInfo aInfo) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ }
+ }
+
+ @Override
public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
synchronized (mGlobalLock) {
return compatibilityInfoForPackageLocked(ai);
@@ -5971,9 +6164,471 @@
mUiHandler.post(() -> {
Dialog d = new FactoryErrorDialog(mUiContext, errorMsg);
d.show();
- mAm.ensureBootCompleted();
+ mAmInternal.ensureBootCompleted();
});
}
}
+
+ @Override
+ public void handleAppDied(WindowProcessController wpc, boolean restarting,
+ Runnable finishInstrumentationCallback) {
+ synchronized (mGlobalLock) {
+ // Remove this application's activities from active lists.
+ boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+
+ wpc.clearRecentTasks();
+ wpc.clearActivities();
+
+ if (wpc.isInstrumenting()) {
+ finishInstrumentationCallback.run();
+ }
+
+ mWindowManager.deferSurfaceLayout();
+ try {
+ if (!restarting && hasVisibleActivities
+ && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+ // If there was nothing to resume, and we are not already restarting this
+ // process, but there is a visible activity that is hosted by the process...
+ // then make sure all visible activities are running, taking care of
+ // restarting this process.
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ }
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+ }
+
+ @Override
+ public void closeSystemDialogs(String reason) {
+ enforceNotIsolatedCaller("closeSystemDialogs");
+
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ // Only allow this from foreground processes, so that background
+ // applications can't abuse it to prevent system UI from being shown.
+ if (uid >= FIRST_APPLICATION_UID) {
+ final WindowProcessController proc = mPidMap.get(pid);
+ if (!proc.isPerceptible()) {
+ Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ + " from background process " + proc);
+ return;
+ }
+ }
+ mWindowManager.closeSystemDialogs(reason);
+
+ mStackSupervisor.closeSystemDialogsLocked();
+ }
+ // Call into AM outside the synchronized block.
+ mAmInternal.broadcastCloseSystemDialogs(reason);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void cleanupDisabledPackageComponents(
+ String packageName, Set<String> disabledClasses, int userId, boolean booted) {
+ synchronized (mGlobalLock) {
+ // Clean-up disabled activities.
+ if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+ packageName, disabledClasses, true, false, userId) && booted) {
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mStackSupervisor.scheduleIdleLocked();
+ }
+
+ // Clean-up disabled tasks
+ getRecentTasks().cleanupDisabledPackageTasksLocked(
+ packageName, disabledClasses, userId);
+ }
+ }
+
+ @Override
+ public boolean onForceStopPackage(String packageName, boolean doit, boolean evenPersistent,
+ int userId) {
+ synchronized (mGlobalLock) {
+
+ boolean didSomething =
+ getActivityStartController().clearPendingActivityLaunches(packageName);
+ didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+ null, doit, evenPersistent, userId);
+ return didSomething;
+ }
+ }
+
+ @Override
+ public void resumeTopActivities(boolean scheduleIdle) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ if (scheduleIdle) {
+ mStackSupervisor.scheduleIdleLocked();
+ }
+ }
+ }
+
+ @Override
+ public void preBindApplication(WindowProcessController wpc) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
+ }
+ }
+
+ @Override
+ public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.attachApplicationLocked(wpc);
+ }
+ }
+
+ @Override
+ public void notifyLockedProfile(@UserIdInt int userId, int currentUserId) {
+ try {
+ if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
+ throw new SecurityException("Only privileged app can call notifyLockedProfile");
+ }
+ } catch (RemoteException ex) {
+ throw new SecurityException("Fail to check is caller a privileged app", ex);
+ }
+
+ synchronized (mGlobalLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mAmInternal.shouldConfirmCredentials(userId)) {
+ if (mKeyguardController.isKeyguardLocked()) {
+ // Showing launcher to avoid user entering credential twice.
+ startHomeActivity(currentUserId, "notifyLockedProfile");
+ }
+ mStackSupervisor.lockAllProfileTasks(userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
+ public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
+ mAmInternal.enforceCallingPermission(
+ MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
+
+ synchronized (mGlobalLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+ FLAG_ACTIVITY_TASK_ON_HOME);
+ ActivityOptions activityOptions = options != null
+ ? new ActivityOptions(options) : ActivityOptions.makeBasic();
+ activityOptions.setLaunchTaskId(
+ mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
+ mContext.startActivityAsUser(intent, activityOptions.toBundle(),
+ UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
+ public void writeActivitiesToProto(ProtoOutputStream proto) {
+ synchronized (mGlobalLock) {
+ // The output proto of "activity --proto activities"
+ // is ActivityManagerServiceDumpActivitiesProto
+ mStackSupervisor.writeToProto(proto,
+ ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
+ }
+ }
+
+ @Override
+ public void saveANRState(String reason) {
+ synchronized (mGlobalLock) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+ pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
+ if (reason != null) {
+ pw.println(" Reason: " + reason);
+ }
+ pw.println();
+ getActivityStartController().dump(pw, " ", null);
+ pw.println();
+ pw.println("-------------------------------------------------------------------------------");
+ dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
+ true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
+ "" /* header */);
+ pw.println();
+ pw.close();
+
+ mLastANRState = sw.toString();
+ }
+ }
+
+ @Override
+ public void clearSavedANRState() {
+ synchronized (mGlobalLock) {
+ mLastANRState = null;
+ }
+ }
+
+ @Override
+ public void dump(String cmd, FileDescriptor fd, PrintWriter pw, String[] args, int opti,
+ boolean dumpAll, boolean dumpClient, String dumpPackage) {
+ synchronized (mGlobalLock) {
+ if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)) {
+ dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+ } else if (DUMP_LASTANR_CMD.equals(cmd)) {
+ dumpLastANRLocked(pw);
+ } else if (DUMP_LASTANR_TRACES_CMD.equals(cmd)) {
+ dumpLastANRTracesLocked(pw);
+ } else if (DUMP_STARTER_CMD.equals(cmd)) {
+ dumpActivityStarterLocked(pw, dumpPackage);
+ } else if (DUMP_CONTAINERS_CMD.equals(cmd)) {
+ dumpActivityContainersLocked(pw);
+ } else if (DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+ if (getRecentTasks() != null) {
+ getRecentTasks().dump(pw, dumpAll, dumpPackage);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean dumpForProcesses(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ String dumpPackage, int dumpAppId, boolean needSep, boolean testPssMode,
+ int wakefulness) {
+ synchronized (mGlobalLock) {
+ if (mHomeProcess != null && (dumpPackage == null
+ || mHomeProcess.mPkgList.contains(dumpPackage))) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mHomeProcess: " + mHomeProcess);
+ }
+ if (mPreviousProcess != null && (dumpPackage == null
+ || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mPreviousProcess: " + mPreviousProcess);
+ }
+ if (dumpAll && (mPreviousProcess == null || dumpPackage == null
+ || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append(" mPreviousProcessVisibleTime: ");
+ TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
+ pw.println(sb);
+ }
+ if (mHeavyWeightProcess != null && (dumpPackage == null
+ || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ }
+ if (dumpPackage == null) {
+ pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
+ mStackSupervisor.dumpDisplayConfigs(pw, " ");
+ }
+ if (dumpAll) {
+ if (dumpPackage == null) {
+ pw.println(" mConfigWillChange: "
+ + getTopDisplayFocusedStack().mConfigWillChange);
+ }
+ if (mCompatModePackages.getPackages().size() > 0) {
+ boolean printed = false;
+ for (Map.Entry<String, Integer> entry
+ : mCompatModePackages.getPackages().entrySet()) {
+ String pkg = entry.getKey();
+ int mode = entry.getValue();
+ if (dumpPackage != null && !dumpPackage.equals(pkg)) {
+ continue;
+ }
+ if (!printed) {
+ pw.println(" mScreenCompatPackages:");
+ printed = true;
+ }
+ pw.println(" " + pkg + ": " + mode);
+ }
+ }
+ }
+
+ if (dumpPackage == null) {
+ pw.println(" mWakefulness="
+ + PowerManagerInternal.wakefulnessToString(wakefulness));
+ pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
+ if (mRunningVoice != null) {
+ pw.println(" mRunningVoice=" + mRunningVoice);
+ pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
+ }
+ pw.println(" mSleeping=" + mSleeping);
+ pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
+ pw.println(" mVrController=" + mVrController);
+ }
+ if (mCurAppTimeTracker != null) {
+ mCurAppTimeTracker.dumpWithHeader(pw, " ", true);
+ }
+ if (mAllowAppSwitchUids.size() > 0) {
+ boolean printed = false;
+ for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
+ ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
+ for (int j = 0; j < types.size(); j++) {
+ if (dumpPackage == null ||
+ UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ if (!printed) {
+ pw.println(" mAllowAppSwitchUids:");
+ printed = true;
+ }
+ pw.print(" User ");
+ pw.print(mAllowAppSwitchUids.keyAt(i));
+ pw.print(": Type ");
+ pw.print(types.keyAt(j));
+ pw.print(" = ");
+ UserHandle.formatUid(pw, types.valueAt(j).intValue());
+ pw.println();
+ }
+ }
+ }
+ }
+ if (dumpPackage == null) {
+ if (mController != null) {
+ pw.println(" mController=" + mController
+ + " mControllerIsAMonkey=" + mControllerIsAMonkey);
+ }
+ pw.println(" mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
+ pw.println(" mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
+ }
+
+ return needSep;
+ }
+ }
+
+ @Override
+ public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) {
+ synchronized (mGlobalLock) {
+ if (dumpPackage == null) {
+ getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION);
+ proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
+ writeSleepStateToProto(proto);
+ if (mController != null) {
+ final long token = proto.start(CONTROLLER);
+ proto.write(CONTROLLER, mController.toString());
+ proto.write(IS_A_MONKEY, mControllerIsAMonkey);
+ proto.end(token);
+ }
+ mStackSupervisor.mGoingToSleep.writeToProto(proto, GOING_TO_SLEEP);
+ mStackSupervisor.mLaunchingActivity.writeToProto(proto, LAUNCHING_ACTIVITY);
+ }
+
+ if (mHomeProcess != null && (dumpPackage == null
+ || mHomeProcess.mPkgList.contains(dumpPackage))) {
+ mHomeProcess.writeToProto(proto, HOME_PROC);
+ }
+
+ if (mPreviousProcess != null && (dumpPackage == null
+ || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ mPreviousProcess.writeToProto(proto, PREVIOUS_PROC);
+ proto.write(PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+ }
+
+ if (mHeavyWeightProcess != null && (dumpPackage == null
+ || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+ mHeavyWeightProcess.writeToProto(proto, 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(SCREEN_COMPAT_PACKAGES);
+ proto.write(PACKAGE, pkg);
+ proto.write(MODE, mode);
+ proto.end(compatToken);
+ }
+ }
+
+ if (mCurAppTimeTracker != null) {
+ mCurAppTimeTracker.writeToProto(proto, CURRENT_TRACKER, true);
+ }
+
+ }
+ }
+
+ @Override
+ public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
+ boolean dumpFocusedStackOnly) {
+ synchronized (mGlobalLock) {
+ return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti,
+ dumpAll, dumpVisibleStacksOnly, dumpFocusedStackOnly);
+ }
+ }
+
+ @Override
+ public boolean canGcNow() {
+ synchronized (mGlobalLock) {
+ return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+ }
+ }
+
+ @Override
+ public WindowProcessController getTopApp() {
+ synchronized (mGlobalLock) {
+ final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+ return top != null ? top.app : null;
+ }
+ }
+
+ @Override
+ public void rankTaskLayersIfNeeded() {
+ synchronized (mGlobalLock) {
+ if (mStackSupervisor != null) {
+ mStackSupervisor.rankTaskLayersIfNeeded();
+ }
+ }
+ }
+
+ @Override
+ public void scheduleDestroyAllActivities(String reason) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+ }
+ }
+
+ @Override
+ public void removeUser(int userId) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.removeUserLocked(userId);
+ }
+ }
+
+ @Override
+ public boolean switchUser(int userId, UserState userState) {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.switchUserLocked(userId, userState);
+ }
+ }
+
+ @Override
+ public void onHandleAppCrash(WindowProcessController wpc) {
+ synchronized (mGlobalLock) {
+ mStackSupervisor.handleAppCrashLocked(wpc);
+ }
+ }
+
+ @Override
+ public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
+ synchronized (mGlobalLock) {
+ return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index cde633d..a80a5b5 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -63,7 +65,7 @@
mService = service;
mProc = data.proc;
mResult = data.result;
- mIsRestartable = (data.task != null || data.isRestartableForService)
+ mIsRestartable = (data.taskId != INVALID_TASK_ID || data.isRestartableForService)
&& Settings.Global.getInt(context.getContentResolver(),
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, 0) != 0;
BidiFormatter bidi = BidiFormatter.getInstance();
@@ -209,7 +211,7 @@
static class Data {
AppErrorResult result;
- TaskRecord task;
+ int taskId;
boolean repeating;
ProcessRecord proc;
boolean isRestartableForService;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6a9c887..e8ec057 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -16,12 +16,13 @@
package com.android.server.am;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -46,21 +47,17 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.os.ProcessCpuTracker;
import com.android.server.RescueParty;
import com.android.server.Watchdog;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
@@ -124,7 +121,7 @@
proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) {
continue;
}
@@ -151,7 +148,7 @@
proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
@@ -184,7 +181,7 @@
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
@@ -214,7 +211,7 @@
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+ final ProcessRecord r = mService.getProcessNames().get(pname, puid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
@@ -411,11 +408,10 @@
}
final int relaunchReason = r != null
- ? r.getWindowProcessController().computeRelaunchReason()
- : ActivityRecord.RELAUNCH_REASON_NONE;
+ ? r.getWindowProcessController().computeRelaunchReason() : RELAUNCH_REASON_NONE;
AppErrorResult result = new AppErrorResult();
- TaskRecord task;
+ int taskId;
synchronized (mService) {
/**
* If crash is handled by instance of {@link android.app.IActivityController},
@@ -428,7 +424,7 @@
// Suppress crash dialog if the process is being relaunched due to a crash during a free
// resize.
- if (relaunchReason == ActivityRecord.RELAUNCH_REASON_FREE_RESIZE) {
+ if (relaunchReason == RELAUNCH_REASON_FREE_RESIZE) {
return;
}
@@ -458,7 +454,7 @@
final Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
- task = data.task;
+ taskId = data.taskId;
msg.obj = data;
mService.mUiHandler.sendMessage(msg);
}
@@ -475,25 +471,15 @@
stopReportingCrashesLocked(r);
}
if (res == AppErrorDialog.RESTART) {
- mService.removeProcessLocked(r, false, true, "crash");
- if (task != null) {
+ mService.mProcessList.removeProcessLocked(r, false, true, "crash");
+ if (taskId != INVALID_TASK_ID) {
try {
- mService.mActivityTaskManager.startActivityFromRecents(task.taskId,
+ mService.mActivityTaskManager.startActivityFromRecents(taskId,
ActivityOptions.makeBasic().toBundle());
} catch (IllegalArgumentException e) {
- // Hmm, that didn't work, app might have crashed before creating a
- // recents entry. Let's see if we have a safe-to-restart intent.
- final Set<String> cats = task.intent != null
- ? task.intent.getCategories() : null;
- if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
- mService.mActivityTaskManager.getActivityStartController().startActivityInPackage(
- task.mCallingUid, callingPid, callingUid, task.mCallingPackage,
- task.intent, null, null, null, 0, 0,
- new SafeActivityOptions(ActivityOptions.makeBasic()),
- task.userId, null,
- "AppErrors", false /*validateIncomingUser*/,
- null /* originatingPendingIntent */);
- }
+ // Hmm...that didn't work. Task should either be in recents or associated
+ // with a stack.
+ Slog.e(TAG, "Could not restart taskId=" + taskId, e);
}
}
}
@@ -501,10 +487,10 @@
long orig = Binder.clearCallingIdentity();
try {
// Kill it with fire!
- mService.mStackSupervisor.handleAppCrashLocked(r.getWindowProcessController());
+ mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
if (!r.isPersistent()) {
- mService.removeProcessLocked(r, false, false, "crash");
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mProcessList.removeProcessLocked(r, false, false, "crash");
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
} finally {
Binder.restoreCallingIdentity(orig);
@@ -565,7 +551,7 @@
} else {
// Huh.
Process.killProcess(pid);
- ActivityManagerService.killProcessGroup(uid, pid);
+ ProcessList.killProcessGroup(uid, pid);
}
}
return true;
@@ -582,27 +568,12 @@
app.setCrashing(true);
app.crashingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
- startAppProblemLocked(app);
+ app.startAppProblemLocked();
app.getWindowProcessController().stopFreezingActivities();
return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
data);
}
- void startAppProblemLocked(ProcessRecord app) {
- // If this app is not running under the current user, then we
- // can't give it a report button because that would require
- // launching the report UI under a different user.
- app.errorReportReceiver = null;
-
- for (int userId : mService.mUserController.getCurrentProfileIds()) {
- if (app.userId == userId) {
- app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
- mContext, app.info.packageName, app.info.flags);
- }
- }
- mService.skipCurrentReceiverLocked(app);
- }
-
/**
* Generate a process error record, suitable for attachment to a ProcessRecord.
*
@@ -616,7 +587,7 @@
*
* @return Returns a fully-formed ProcessErrorStateInfo record.
*/
- private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
+ ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
@@ -684,7 +655,7 @@
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
final boolean procIsBoundForeground =
- (app.curProcState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ (app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
Long crashTime;
Long crashTimePersistent;
@@ -723,7 +694,7 @@
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
- mService.mStackSupervisor.handleAppCrashLocked(app.getWindowProcessController());
+ mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
if (!app.isPersistent()) {
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
@@ -743,18 +714,18 @@
// Don't let services in this process be restarted and potentially
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
- mService.removeProcessLocked(app, false, tryAgain, "crash");
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mProcessList.removeProcessLocked(app, false, tryAgain, "crash");
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
if (!showBackground) {
return false;
}
}
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
} else {
- final TaskRecord affectedTask =
- mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app.getWindowProcessController(), reason);
+ final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
+ app.getWindowProcessController(), reason);
if (data != null) {
- data.task = affectedTask;
+ data.taskId = affectedTaskId;
}
if (data != null && crashTimePersistent != null
&& now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
@@ -854,259 +825,13 @@
}
}
- void stopReportingCrashesLocked(ProcessRecord proc) {
+ private void stopReportingCrashesLocked(ProcessRecord proc) {
if (mAppsNotReportingCrashes == null) {
mAppsNotReportingCrashes = new ArraySet<>();
}
mAppsNotReportingCrashes.add(proc.info.packageName);
}
- static boolean isInterestingForBackgroundTraces(ProcessRecord app) {
- // The system_server is always considered interesting.
- if (app.pid == MY_PID) {
- return true;
- }
-
- // A package is considered interesting if any of the following is true :
- //
- // - It's displaying an activity.
- // - It's the SystemUI.
- // - It has an overlay or a top UI visible.
- //
- // NOTE: The check whether a given ProcessRecord belongs to the systemui
- // process is a bit of a kludge, but the same pattern seems repeated at
- // several places in the system server.
- return app.isInterestingToUserLocked() ||
- (app.info != null && "com.android.systemui".equals(app.info.packageName)) ||
- (app.hasTopUi || app.hasOverlayUi);
- }
-
- final void appNotResponding(ProcessRecord app, ActivityRecord activity,
- ActivityRecord parent, boolean aboveSystem, final String annotation) {
- ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
- SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
-
- if (mService.mActivityTaskManager.mController != null) {
- try {
- // 0 == continue, -1 = kill process immediately
- int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
- app.processName, app.pid, annotation);
- if (res < 0 && app.pid != MY_PID) {
- app.kill("anr", true);
- }
- } catch (RemoteException e) {
- mService.mActivityTaskManager.mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
- }
-
- long anrTime = SystemClock.uptimeMillis();
- if (ActivityManagerService.MONITOR_CPU_USAGE) {
- mService.updateCpuStatsNow();
- }
-
- // Unless configured otherwise, swallow ANRs in background processes & kill the process.
- boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-
- boolean isSilentANR;
-
- synchronized (mService) {
- // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
- if (mService.mActivityTaskManager.mShuttingDown) {
- Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
- return;
- } else if (app.isNotResponding()) {
- Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
- return;
- } else if (app.isCrashing()) {
- Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
- return;
- } else if (app.killedByAm) {
- Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);
- return;
- } else if (app.killed) {
- Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation);
- return;
- }
-
- // In case we come through here for the same app before completing
- // this one, mark as anring now so we will bail out.
- app.setNotResponding(true);
-
- // Log the ANR to the event log.
- EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
- app.processName, app.info.flags, annotation);
-
- // Dump thread traces as quickly as we can, starting with "interesting" processes.
- firstPids.add(app.pid);
-
- // Don't dump other PIDs if it's a background ANR
- isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
- if (!isSilentANR) {
- int parentPid = app.pid;
- if (parent != null && parent.app != null && parent.app.getPid() > 0) {
- parentPid = parent.app.getPid();
- }
- if (parentPid != app.pid) firstPids.add(parentPid);
-
- if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
-
- for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord r = mService.mLruProcesses.get(i);
- if (r != null && r.thread != null) {
- int pid = r.pid;
- if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
- if (r.isPersistent()) {
- firstPids.add(pid);
- if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
- } else if (r.treatLikeActivity) {
- firstPids.add(pid);
- if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
- } else {
- lastPids.put(pid, Boolean.TRUE);
- if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
- }
- }
- }
- }
- }
- }
-
- // Log the ANR to the main log.
- StringBuilder info = new StringBuilder();
- info.setLength(0);
- info.append("ANR in ").append(app.processName);
- if (activity != null && activity.shortComponentName != null) {
- info.append(" (").append(activity.shortComponentName).append(")");
- }
- info.append("\n");
- info.append("PID: ").append(app.pid).append("\n");
- if (annotation != null) {
- info.append("Reason: ").append(annotation).append("\n");
- }
- if (parent != null && parent != activity) {
- info.append("Parent: ").append(parent.shortComponentName).append("\n");
- }
-
- ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
-
- // don't dump native PIDs for background ANRs unless it is the process of interest
- String[] nativeProcs = null;
- if (isSilentANR) {
- for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
- if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {
- nativeProcs = new String[] { app.processName };
- break;
- }
- }
- } else {
- nativeProcs = NATIVE_STACKS_OF_INTEREST;
- }
-
- int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
- ArrayList<Integer> nativePids = null;
-
- if (pids != null) {
- nativePids = new ArrayList<Integer>(pids.length);
- for (int i : pids) {
- nativePids.add(i);
- }
- }
-
- // For background ANRs, don't pass the ProcessCpuTracker to
- // avoid spending 1/2 second collecting stats to rank lastPids.
- File tracesFile = ActivityManagerService.dumpStackTraces(
- firstPids,
- (isSilentANR) ? null : processCpuTracker,
- (isSilentANR) ? null : lastPids,
- nativePids);
-
- String cpuInfo = null;
- if (ActivityManagerService.MONITOR_CPU_USAGE) {
- mService.updateCpuStatsNow();
- synchronized (mService.mProcessCpuTracker) {
- cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
- }
- info.append(processCpuTracker.printCurrentLoad());
- info.append(cpuInfo);
- }
-
- info.append(processCpuTracker.printCurrentState(anrTime));
-
- Slog.e(TAG, info.toString());
- if (tracesFile == null) {
- // There is no trace file, so dump (only) the alleged culprit's threads to the log
- Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
- }
-
- StatsLog.write(StatsLog.ANR_OCCURRED, app.uid, app.processName,
- activity == null ? "unknown": activity.shortComponentName, annotation,
- (app.info != null) ? (app.info.isInstantApp()
- ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
- : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
- : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
- app != null ? (app.isInterestingToUserLocked()
- ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
- : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND)
- : StatsLog.ANROCCURRED__FOREGROUND_STATE__UNKNOWN);
- mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
- cpuInfo, tracesFile, null);
-
- if (mService.mActivityTaskManager.mController != null) {
- try {
- // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
- int res = mService.mActivityTaskManager.mController.appNotResponding(
- app.processName, app.pid, info.toString());
- if (res != 0) {
- if (res < 0 && app.pid != MY_PID) {
- app.kill("anr", true);
- } else {
- synchronized (mService) {
- mService.mServices.scheduleServiceTimeoutLocked(app);
- }
- }
- return;
- }
- } catch (RemoteException e) {
- mService.mActivityTaskManager.mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
- }
-
- synchronized (mService) {
- mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
-
- if (isSilentANR) {
- app.kill("bg anr", true);
- return;
- }
-
- // Set the app's notResponding state, and look up the errorReportReceiver
- makeAppNotRespondingLocked(app,
- activity != null ? activity.shortComponentName : null,
- annotation != null ? "ANR " + annotation : "ANR",
- info.toString());
-
- // Bring up the infamous App Not Responding dialog
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
- msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
-
- mService.mUiHandler.sendMessage(msg);
- }
- }
-
- private void makeAppNotRespondingLocked(ProcessRecord app,
- String activity, String shortMsg, String longMsg) {
- app.setNotResponding(true);
- app.notRespondingReport = generateProcessError(app,
- ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
- activity, shortMsg, longMsg, null);
- startAppProblemLocked(app);
- app.getWindowProcessController().stopFreezingActivities();
- }
-
void handleShowAnrUi(Message msg) {
Dialog dialogToShow = null;
synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 7c983ff..cb76e2f 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.content.pm.ApplicationInfo;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -58,8 +59,8 @@
setCancelable(false);
int resid;
- CharSequence name1 = data.activity != null
- ? data.activity.info.loadLabel(context.getPackageManager())
+ CharSequence name1 = data.aInfo != null
+ ? data.aInfo.loadLabel(context.getPackageManager())
: null;
CharSequence name2 = null;
if ((mProc.pkgList.size() == 1) &&
@@ -181,12 +182,12 @@
static class Data {
final ProcessRecord proc;
- final ActivityRecord activity;
+ final ApplicationInfo aInfo;
final boolean aboveSystem;
- Data(ProcessRecord proc, ActivityRecord activity, boolean aboveSystem) {
+ Data(ProcessRecord proc, ApplicationInfo aInfo, boolean aboveSystem) {
this.proc = proc;
- this.activity = activity;
+ this.aInfo = aInfo;
this.aboveSystem = aboveSystem;
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e2035f6..a0977be 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -191,7 +191,7 @@
@Override
public void run() {
- mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
+ mApp.appNotResponding(null, null, null, null, false, mAnnotation);
}
}
@@ -286,7 +286,7 @@
r.curApp = app;
app.curReceivers.add(r);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
- mService.updateLruProcessLocked(app, false, null);
+ mService.mProcessList.updateLruProcessLocked(app, false, null);
if (!skipOomAdj) {
mService.updateOomAdjLocked();
}
@@ -892,7 +892,7 @@
isDead = proc == null || proc.isCrashing();
}
} else {
- final ProcessRecord proc = mService.mProcessNames.get(
+ final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.pendingStart;
}
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 536f3a9..3c4ab00 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -16,8 +16,11 @@
package com.android.server.am;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import java.io.File;
import java.io.FileInputStream;
@@ -48,7 +51,7 @@
import android.util.Xml;
public final class CompatModePackages {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private final ActivityTaskManagerService mService;
@@ -61,7 +64,7 @@
private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
- private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
+ private static final int MSG_WRITE = 300;
private final CompatHandler mHandler;
@@ -321,16 +324,16 @@
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
- for (int i = mService.mAm.mLruProcesses.size() - 1; i >= 0; i--) {
- final ProcessRecord app = mService.mAm.mLruProcesses.get(i);
- if (!app.pkgList.containsKey(packageName)) {
+ for (int i = mService.mPidMap.size() - 1; i >= 0; i--) {
+ final WindowProcessController app = mService.mPidMap.valueAt(i);
+ if (!app.mPkgList.contains(packageName)) {
continue;
}
try {
- if (app.thread != null) {
+ if (app.hasThread()) {
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
- + app.processName + " new compat " + ci);
- app.thread.updatePackageCompatibilityInfo(packageName, ci);
+ + app.mName + " new compat " + ci);
+ app.getThread().updatePackageCompatibilityInfo(packageName, ci);
}
} catch (Exception e) {
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 09c152e..48e26ed 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -56,6 +56,10 @@
sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.ANGLE_ENABLED_APP, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
// add other global settings here...
}
@@ -122,6 +126,7 @@
value = Settings.Global.getString(context.getContentResolver(), setting);
}
if (value == null) {
+ snapshot.remove(setting);
continue;
}
Class<?> type = entry.getValue();
diff --git a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
index e5add58..b39873f 100644
--- a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
+++ b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
@@ -16,8 +16,8 @@
package com.android.server.am;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.AlertDialog;
import android.content.Context;
@@ -34,7 +34,7 @@
import com.android.server.utils.AppInstallerUtil;
public class DeprecatedTargetSdkVersionDialog {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_ATM;
private final AlertDialog mDialog;
private final String mPackageName;
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index cfe2829..28b2a42 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -30,9 +30,9 @@
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
@@ -60,7 +60,7 @@
*/
class KeyguardController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
private final ActivityStackSupervisor mStackSupervisor;
private WindowManagerService mWindowManager;
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 218d908..68e897f 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
@@ -97,6 +98,15 @@
break;
}
}
+
+ if (activity != null && activity.requestedVrComponent != null) {
+ // Check if the Activity is a VR activity. If so, it should be launched in main display.
+ result.mPreferredDisplayId = DEFAULT_DISPLAY;
+ } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
+ // Get the virtual display ID from ActivityTaskManagerService. If that's set we
+ // should always use that.
+ result.mPreferredDisplayId = mService.mVr2dDisplayId;
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 643c922..5b31d5f 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -28,10 +28,10 @@
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -84,7 +84,7 @@
* @see Activity#stopLockTask()
*/
public class LockTaskController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_ATM;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
@VisibleForTesting
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index a8e1ccc..85ee7e6 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -16,7 +16,7 @@
package com.android.server.am;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +37,38 @@
* Static utility methods related to {@link MemoryStat}.
*/
final class MemoryStatUtil {
+ /**
+ * Which native processes to create {@link MemoryStat} for.
+ *
+ * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
+ * /system/bin/statsd for the stats daemon.
+ */
+ static final String[] MEMORY_STAT_INTERESTING_NATIVE_PROCESSES = new String[]{
+ "/system/bin/statsd", // Stats daemon.
+ "/system/bin/surfaceflinger",
+ "/system/bin/apexd", // APEX daemon.
+ "/system/bin/audioserver",
+ "/system/bin/cameraserver",
+ "/system/bin/drmserver",
+ "/system/bin/healthd",
+ "/system/bin/incidentd",
+ "/system/bin/installd",
+ "/system/bin/lmkd", // Low memory killer daemon.
+ "/system/bin/logd",
+ "media.codec",
+ "media.extractor",
+ "media.metrics",
+ "/system/bin/mediadrmserver",
+ "/system/bin/mediaserver",
+ "/system/bin/performanced",
+ "/system/bin/tombstoned",
+ "/system/bin/traced", // Perfetto.
+ "/system/bin/traced_probes", // Perfetto.
+ "webview_zygote",
+ "zygote",
+ "zygote64",
+ };
+
static final int BYTES_IN_KILOBYTE = 1024;
static final int PAGE_SIZE = 4096;
@@ -57,6 +89,8 @@
private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
/** Path to procfs status file for logging app memory state */
private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
+ /** Path to procfs cmdline file. Used with pid: /proc/pid/cmdline. */
+ private static final String PROC_CMDLINE_FILE_FMT = "/proc/%d/cmdline";
private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -119,6 +153,18 @@
return stat;
}
+ /**
+ * Reads cmdline of a process from procfs.
+ *
+ * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
+ * if the file is not available.
+ */
+ static String readCmdlineFromProcfs(int pid) {
+ String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
+ String cmdline = readFileContents(path);
+ return cmdline != null ? cmdline : "";
+ }
+
private static String readFileContents(String path) {
final File file = new File(path);
if (!file.exists()) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index b9c6fa6..2dcddff 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -288,12 +288,19 @@
resolvedType = key.requestResolvedType;
}
+ // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
+ // can specify a consistent launch mode even if the PendingIntent is immutable
+ final ActivityOptions opts = ActivityOptions.fromBundle(options);
+ if (opts != null) {
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ }
+
// Extract options before clearing calling identity
mergedOptions = key.options;
if (mergedOptions == null) {
- mergedOptions = SafeActivityOptions.fromBundle(options);
+ mergedOptions = new SafeActivityOptions(opts);
} else {
- mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+ mergedOptions.setCallerOptions(opts);
}
if (whitelistDuration != null) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3ac7885..cb6061a 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -16,28 +16,94 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.os.Process.FIRST_ISOLATED_UID;
+import static android.os.Process.LAST_ISOLATED_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.getFreeMemory;
+import static android.os.Process.getTotalMemory;
+import static android.os.Process.killProcessQuiet;
+import static android.os.Process.startWebView;
+import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_WITH_WRAPPER;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.TAG_LRU;
+import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
+import static com.android.server.am.ActivityManagerService.TAG_PSS;
+import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.AppProtoEnums;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.StatsLog;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.Zygote;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.MemInfoReader;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.Watchdog;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
-
-import android.app.ActivityManager;
-import android.app.AppProtoEnums;
-import android.os.Build;
-import android.os.SystemClock;
-import com.android.internal.util.MemInfoReader;
-import com.android.server.wm.WindowManagerService;
-
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.os.SystemProperties;
-import android.net.LocalSocketAddress;
-import android.net.LocalSocket;
-import android.util.Slog;
-import android.view.Display;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Activity manager code dealing with processes.
@@ -47,7 +113,7 @@
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
- static final int MIN_CRASH_INTERVAL = 60*1000;
+ static final int MIN_CRASH_INTERVAL = 60 * 1000;
// OOM adjustments for processes in various states:
@@ -129,7 +195,7 @@
static final int NATIVE_ADJ = -1000;
// Memory pages are 4K.
- static final int PAGE_SIZE = 4*1024;
+ static final int PAGE_SIZE = 4 * 1024;
// Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
static final int SCHED_GROUP_BACKGROUND = 0;
@@ -148,7 +214,7 @@
static final int MIN_CACHED_APPS = 2;
// We allow empty processes to stick around for at most 30 minutes.
- static final long MAX_EMPTY_TIME = 30*60*1000;
+ static final long MAX_EMPTY_TIME = 30 * 60 * 1000;
// Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_CRITICAL_THRESHOLD = 3;
@@ -162,9 +228,17 @@
// LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
// LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
+ // LMK_PROCPURGE
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
+ static final byte LMK_PROCPURGE = 3;
+
+ ActivityManagerService mService = null;
+
+ // To kill process groups asynchronously
+ static KillHandler sKillHandler = null;
+ static ServiceThread sKillThread = null;
// These are the various interesting memory levels that we will give to
// the OOM killer. Note that the OOM killer only supports 6 slots, so we
@@ -197,6 +271,123 @@
private static LocalSocket sLmkdSocket;
private static OutputStream sLmkdOutputStream;
+ /**
+ * Temporary to avoid allocations. Protected by main lock.
+ */
+ @GuardedBy("mService")
+ final StringBuilder mStringBuilder = new StringBuilder(256);
+
+ /**
+ * A global counter for generating sequence numbers.
+ * This value will be used when incrementing sequence numbers in individual uidRecords.
+ *
+ * Having a global counter ensures that seq numbers are monotonically increasing for a
+ * particular uid even when the uidRecord is re-created.
+ */
+ @GuardedBy("mService")
+ @VisibleForTesting
+ long mProcStateSeqCounter = 0;
+
+ /**
+ * A global counter for generating sequence numbers to uniquely identify pending process starts.
+ */
+ @GuardedBy("mService")
+ private long mProcStartSeqCounter = 0;
+
+ /**
+ * Contains {@link ProcessRecord} objects for pending process starts.
+ *
+ * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
+ */
+ @GuardedBy("mService")
+ final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
+
+ /**
+ * List of running applications, sorted by recent usage.
+ * The first entry in the list is the least recently used.
+ */
+ final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
+
+ /**
+ * Where in mLruProcesses that the processes hosting activities start.
+ */
+ int mLruProcessActivityStart = 0;
+
+ /**
+ * Where in mLruProcesses that the processes hosting services start.
+ * This is after (lower index) than mLruProcessesActivityStart.
+ */
+ int mLruProcessServiceStart = 0;
+
+ /**
+ * Current sequence id for process LRU updating.
+ */
+ int mLruSeq = 0;
+
+ /**
+ * The currently running isolated processes.
+ */
+ final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
+
+ /**
+ * Counter for assigning isolated process uids, to avoid frequently reusing the
+ * same ones.
+ */
+ int mNextIsolatedProcessUid = 0;
+
+ /**
+ * Processes that are being forcibly torn down.
+ */
+ final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
+
+ /**
+ * All of the applications we currently have running organized by name.
+ * The keys are strings of the application package name (as
+ * returned by the package manager), and the keys are ApplicationRecord
+ * objects.
+ */
+ final MyProcessMap mProcessNames = new MyProcessMap();
+
+ final class MyProcessMap extends ProcessMap<ProcessRecord> {
+ @Override
+ public ProcessRecord put(String name, int uid, ProcessRecord value) {
+ final ProcessRecord r = super.put(name, uid, value);
+ mService.mAtmInternal.onProcessAdded(r.getWindowProcessController());
+ return r;
+ }
+
+ @Override
+ public ProcessRecord remove(String name, int uid) {
+ final ProcessRecord r = super.remove(name, uid);
+ mService.mAtmInternal.onProcessRemoved(name, uid);
+ return r;
+ }
+ }
+
+ final class KillHandler extends Handler {
+ static final int KILL_PROCESS_GROUP_MSG = 4000;
+
+ public KillHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case KILL_PROCESS_GROUP_MSG:
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
+ Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+ //////////////////// END FIELDS ////////////////////
+
ProcessList() {
MemInfoReader minfo = new MemInfoReader();
minfo.readMemInfo();
@@ -204,6 +395,16 @@
updateOomLevels(0, 0, false);
}
+ void init(ActivityManagerService service) {
+ mService = service;
+ if (sKillHandler == null) {
+ sKillThread = new ServiceThread(TAG + ":kill",
+ THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ sKillThread.start();
+ sKillHandler = new KillHandler(sKillThread.getLooper());
+ }
+ }
+
void applyDisplaySize(WindowManagerService wm) {
if (!mHaveDisplaySize) {
Point p = new Point();
@@ -219,12 +420,12 @@
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
// Scale buckets from avail memory: at 300MB we use the lowest values to
// 700MB or more for the top values.
- float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
+ float scaleMem = ((float) (mTotalMemMb - 350)) / (700 - 350);
// Scale buckets from screen size.
- int minSize = 480*800; // 384000
- int maxSize = 1280*800; // 1024000 230400 870400 .264
- float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
+ int minSize = 480 * 800; // 384000
+ int maxSize = 1280 * 800; // 1024000 230400 870400 .264
+ float scaleDisp = ((float)(displayWidth * displayHeight) - minSize) / (maxSize - minSize);
if (false) {
Slog.i("XXXXXX", "scaleMem=" + scaleMem);
Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
@@ -244,27 +445,27 @@
final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
- for (int i=0; i<mOomAdj.length; i++) {
+ for (int i = 0; i < mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
if (is64bit) {
// Increase the high min-free levels for cached processes for 64-bit
- if (i == 4) high = (high*3)/2;
- else if (i == 5) high = (high*7)/4;
+ if (i == 4) high = (high * 3) / 2;
+ else if (i == 5) high = (high * 7) / 4;
}
- mOomMinFree[i] = (int)(low + ((high-low)*scale));
+ mOomMinFree[i] = (int)(low + ((high - low) * scale));
}
if (minfree_abs >= 0) {
- for (int i=0; i<mOomAdj.length; i++) {
+ for (int i = 0; i < mOomAdj.length; i++) {
mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
/ mOomMinFree[mOomAdj.length - 1]);
}
}
if (minfree_adj != 0) {
- for (int i=0; i<mOomAdj.length; i++) {
- mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
+ for (int i = 0; i < mOomAdj.length; i++) {
+ mOomMinFree[i] += (int)((float) minfree_adj * mOomMinFree[i]
/ mOomMinFree[mOomAdj.length - 1]);
if (mOomMinFree[i] < 0) {
mOomMinFree[i] = 0;
@@ -275,13 +476,15 @@
// The maximum size we will restore a process from cached to background, when under
// memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
// before killing background processes.
- mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;
+ mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024) / 3;
// Ask the kernel to try to keep enough memory free to allocate 3 full
// screen 32bpp buffers without entering direct reclaim.
int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
- int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
- int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
+ int reserve_adj = Resources.getSystem().getInteger(
+ com.android.internal.R.integer.config_extraFreeKbytesAdjust);
+ int reserve_abs = Resources.getSystem().getInteger(
+ com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
if (reserve_abs >= 0) {
reserve = reserve_abs;
@@ -295,10 +498,10 @@
}
if (write) {
- ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
+ ByteBuffer buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
buf.putInt(LMK_TARGET);
- for (int i=0; i<mOomAdj.length; i++) {
- buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
+ for (int i = 0; i < mOomAdj.length; i++) {
+ buf.putInt((mOomMinFree[i] * 1024)/PAGE_SIZE);
buf.putInt(mOomAdj[i]);
}
@@ -318,7 +521,7 @@
if (space == null) return prefix;
return prefix + " ";
}
- return prefix + "+" + Integer.toString(val-base);
+ return prefix + "+" + Integer.toString(val - base);
}
public static String makeOomAdjString(int setAdj) {
@@ -475,7 +678,7 @@
}
public static void appendRamKb(StringBuilder sb, long ramKb) {
- for (int j=0, fact=10; j<6; j++, fact*=10) {
+ for (int j = 0, fact = 10; j < 6; j++, fact *= 10) {
if (ramKb < fact) {
sb.append(' ');
}
@@ -735,12 +938,12 @@
}
long getMemLevel(int adjustment) {
- for (int i=0; i<mOomAdj.length; i++) {
+ for (int i = 0; i < mOomAdj.length; i++) {
if (adjustment <= mOomAdj[i]) {
return mOomMinFree[i] * 1024;
}
}
- return mOomMinFree[mOomAdj.length-1] * 1024;
+ return mOomMinFree[mOomAdj.length - 1] * 1024;
}
/**
@@ -813,31 +1016,1563 @@
return true;
}
+ // Never call directly, use writeLmkd() instead
+ private static boolean writeLmkdCommand(ByteBuffer buf) {
+ try {
+ sLmkdOutputStream.write(buf.array(), 0, buf.position());
+ } catch (IOException ex) {
+ Slog.w(TAG, "Error writing to lowmemorykiller socket");
+
+ try {
+ sLmkdSocket.close();
+ } catch (IOException ex2) {
+ }
+
+ sLmkdSocket = null;
+ return false;
+ }
+ return true;
+ }
+
private static void writeLmkd(ByteBuffer buf) {
for (int i = 0; i < 3; i++) {
if (sLmkdSocket == null) {
- if (openLmkdSocket() == false) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ie) {
- }
- continue;
+ if (openLmkdSocket() == false) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
}
- }
-
- try {
- sLmkdOutputStream.write(buf.array(), 0, buf.position());
- return;
- } catch (IOException ex) {
- Slog.w(TAG, "Error writing to lowmemorykiller socket");
-
- try {
- sLmkdSocket.close();
- } catch (IOException ex2) {
+ continue;
}
- sLmkdSocket = null;
+ // Purge any previously registered pids
+ ByteBuffer purge_buf = ByteBuffer.allocate(4);
+ purge_buf.putInt(LMK_PROCPURGE);
+ if (writeLmkdCommand(purge_buf) == false) {
+ // Write failed, skip the rest and retry
+ continue;
+ }
+ }
+ if (writeLmkdCommand(buf)) {
+ return;
+ }
+ }
+ }
+
+ static void killProcessGroup(int uid, int pid) {
+ /* static; one-time init here */
+ if (sKillHandler != null) {
+ sKillHandler.sendMessage(
+ sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
+ } else {
+ Slog.w(TAG, "Asked to kill process group before system bringup!");
+ Process.killProcessGroup(uid, pid);
+ }
+ }
+
+ final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
+ keepIfLarge) {
+ if (uid == SYSTEM_UID) {
+ // The system gets to run in any process. If there are multiple
+ // processes with the same uid, just pick the first (this
+ // should never happen).
+ SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
+ if (procs == null) return null;
+ final int procCount = procs.size();
+ for (int i = 0; i < procCount; i++) {
+ final int procUid = procs.keyAt(i);
+ if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
+ // Don't use an app process or different user process for system component.
+ continue;
+ }
+ return procs.valueAt(i);
+ }
+ }
+ ProcessRecord proc = mProcessNames.get(processName, uid);
+ if (false && proc != null && !keepIfLarge
+ && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ && proc.lastCachedPss >= 4000) {
+ // Turn this condition on to cause killing to happen regularly, for testing.
+ if (proc.baseProcessTracker != null) {
+ proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ proc.lastCachedPss, holder.appVersion);
+ }
+ }
+ proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
+ } else if (proc != null && !keepIfLarge
+ && mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+ && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+ if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
+ .lastCachedPss);
+ if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) {
+ if (proc.baseProcessTracker != null) {
+ proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
+ proc.lastCachedPss);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ proc.lastCachedPss, holder.appVersion);
+ }
+ }
+ proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
+ }
+ }
+ return proc;
+ }
+
+ void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
+ final long homeAppMem = getMemLevel(HOME_APP_ADJ);
+ final long cachedAppMem = getMemLevel(CACHED_APP_MIN_ADJ);
+ outInfo.availMem = getFreeMemory();
+ outInfo.totalMem = getTotalMemory();
+ outInfo.threshold = homeAppMem;
+ outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
+ outInfo.hiddenAppThreshold = cachedAppMem;
+ outInfo.secondaryServerThreshold = getMemLevel(SERVICE_ADJ);
+ outInfo.visibleAppThreshold = getMemLevel(VISIBLE_APP_ADJ);
+ outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
+ }
+
+ ProcessRecord findAppProcessLocked(IBinder app, String reason) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final int NA = apps.size();
+ for (int ia = 0; ia < NA; ia++) {
+ ProcessRecord p = apps.valueAt(ia);
+ if (p.thread != null && p.thread.asBinder() == app) {
+ return p;
+ }
+ }
+ }
+
+ Slog.w(TAG, "Can't find mystery application for " + reason
+ + " from pid=" + Binder.getCallingPid()
+ + " uid=" + Binder.getCallingUid() + ": " + app);
+ return null;
+ }
+
+ private void checkSlow(long startTime, String where) {
+ long now = SystemClock.uptimeMillis();
+ if ((now - startTime) > 50) {
+ // If we are taking more than 50ms, log about it.
+ Slog.w(TAG, "Slow operation: " + (now - startTime) + "ms so far, now at " + where);
+ }
+ }
+
+ /**
+ * @return {@code true} if process start is successful, false otherwise.
+ * @param app
+ * @param hostingType
+ * @param hostingNameStr
+ * @param disableHiddenApiChecks
+ * @param abiOverride
+ */
+ @GuardedBy("mService")
+ boolean startProcessLocked(ProcessRecord app, String hostingType,
+ String hostingNameStr, boolean disableHiddenApiChecks, boolean mountExtStorageFull,
+ String abiOverride) {
+ if (app.pendingStart) {
+ return true;
+ }
+ long startTime = SystemClock.elapsedRealtime();
+ if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
+ checkSlow(startTime, "startProcess: removing from pids map");
+ synchronized (mService.mPidsSelfLocked) {
+ mService.mPidsSelfLocked.remove(app.pid);
+ mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+ }
+ checkSlow(startTime, "startProcess: done removing from pids map");
+ app.setPid(0);
+ }
+
+ if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
+ TAG_PROCESSES,
+ "startProcessLocked removing on hold: " + app);
+ mService.mProcessesOnHold.remove(app);
+
+ checkSlow(startTime, "startProcess: starting to update cpu stats");
+ mService.updateCpuStats();
+ checkSlow(startTime, "startProcess: done updating cpu stats");
+
+ try {
+ try {
+ final int userId = UserHandle.getUserId(app.uid);
+ AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ int uid = app.uid;
+ int[] gids = null;
+ int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+ if (!app.isolated) {
+ int[] permGids = null;
+ try {
+ checkSlow(startTime, "startProcess: getting gids from package manager");
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ permGids = pm.getPackageGids(app.info.packageName,
+ MATCH_DIRECT_BOOT_AUTO, app.userId);
+ if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)
+ && mountExtStorageFull) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
+ } else {
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
+ mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
+ app.info.packageName);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ /*
+ * Add shared application and profile GIDs so applications can share some
+ * resources like shared libraries and access user-wide resources
+ */
+ if (ArrayUtils.isEmpty(permGids)) {
+ gids = new int[3];
+ } else {
+ gids = new int[permGids.length + 3];
+ System.arraycopy(permGids, 0, gids, 3, permGids.length);
+ }
+ gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
+ gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+ gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
+ // Replace any invalid GIDs
+ if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
+ if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
+ }
+ checkSlow(startTime, "startProcess: building args");
+ if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
+ uid = 0;
+ }
+ int runtimeFlags = 0;
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+ runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
+ // Also turn on CheckJNI for debuggable apps. It's quite
+ // awkward to turn on otherwise.
+ runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+ }
+ // Run the app in safe mode if its manifest requests so or the
+ // system is booted in safe mode.
+ if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
+ mService.mSafeMode == true) {
+ runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
+ }
+ if ("1".equals(SystemProperties.get("debug.checkjni"))) {
+ runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+ }
+ String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
+ }
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
+ if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
+ runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
+ }
+ if ("1".equals(SystemProperties.get("debug.assert"))) {
+ runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
+ }
+ if (mService.mNativeDebuggingApp != null
+ && mService.mNativeDebuggingApp.equals(app.processName)) {
+ // Enable all debug flags required by the native debugger.
+ runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
+ runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
+ runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
+ mService.mNativeDebuggingApp = null;
+ }
+
+ if (app.info.isPrivilegedApp() &&
+ DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
+ runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
+ }
+
+ if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
+ app.info.maybeUpdateHiddenApiEnforcementPolicy(
+ mService.mHiddenApiBlacklist.getPolicyForPrePApps(),
+ mService.mHiddenApiBlacklist.getPolicyForPApps());
+ @ApplicationInfo.HiddenApiEnforcementPolicy int policy =
+ app.info.getHiddenApiEnforcementPolicy();
+ int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
+ if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
+ throw new IllegalStateException("Invalid API policy: " + policy);
+ }
+ runtimeFlags |= policyBits;
+ }
+
+ String invokeWith = null;
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ // Debuggable apps may include a wrapper script with their library directory.
+ String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ if (new File(wrapperFileName).exists()) {
+ invokeWith = "/system/bin/logwrapper " + wrapperFileName;
+ }
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
+ if (requiredAbi == null) {
+ requiredAbi = Build.SUPPORTED_ABIS[0];
+ }
+
+ String instructionSet = null;
+ if (app.info.primaryCpuAbi != null) {
+ instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
+ }
+
+ app.gids = gids;
+ app.setRequiredAbi(requiredAbi);
+ app.instructionSet = instructionSet;
+
+ // the per-user SELinux context must be set
+ if (TextUtils.isEmpty(app.info.seInfoUser)) {
+ Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
+ new IllegalStateException("SELinux tag not defined for "
+ + app.info.packageName + " (uid " + app.uid + ")"));
+ }
+ final String seInfo = app.info.seInfo
+ + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
+ // Start the process. It will either succeed and return a result containing
+ // the PID of the new process, or else throw a RuntimeException.
+ final String entryPoint = "android.app.ActivityThread";
+
+ return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
+ runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
+ startTime);
+ } catch (RuntimeException e) {
+ Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);
+
+ // Something went very wrong while trying to start this process; one
+ // common case is when the package is frozen due to an active
+ // upgrade. To recover, clean up any active bookkeeping related to
+ // starting this process. (We already invoked this method once when
+ // the package was initially frozen through KILL_APPLICATION_MSG, so
+ // it doesn't hurt to use it again.)
+ mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+ false, false, true, false, false, app.userId, "start failure");
+ return false;
+ }
+ }
+
+ @GuardedBy("mService")
+ boolean startProcessLocked(String hostingType, String hostingNameStr,
+ String entryPoint,
+ ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+ String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+ long startTime) {
+ app.pendingStart = true;
+ app.killedByAm = false;
+ app.removed = false;
+ app.killed = false;
+ final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+ app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
+ if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
+ if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
+ "Posting procStart msg for " + app.toShortString());
+ mService.mProcStartHandler.post(() -> {
+ try {
+ synchronized (mService) {
+ final String reason = isProcStartValidLocked(app, startSeq);
+ if (reason != null) {
+ Slog.w(TAG_PROCESSES, app + " not valid anymore,"
+ + " don't start process, " + reason);
+ app.pendingStart = false;
+ return;
+ }
+ app.setUsingWrapper(invokeWith != null
+ || SystemProperties.get("wrap." + app.processName) != null);
+ mPendingStarts.put(startSeq, app);
+ }
+ final Process.ProcessStartResult startResult = startProcess(app.hostingType,
+ entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
+ app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
+ synchronized (mService) {
+ handleProcessStartedLocked(app, startResult, startSeq);
+ }
+ } catch (RuntimeException e) {
+ synchronized (mService) {
+ Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ + app.processName, e);
+ mPendingStarts.remove(startSeq);
+ app.pendingStart = false;
+ mService.forceStopPackageLocked(app.info.packageName,
+ UserHandle.getAppId(app.uid),
+ false, false, true, false, false, app.userId, "start failure");
+ }
+ }
+ });
+ return true;
+ } else {
+ try {
+ final Process.ProcessStartResult startResult = startProcess(hostingType,
+ entryPoint, app,
+ uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
+ invokeWith, startTime);
+ handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
+ startSeq, false);
+ } catch (RuntimeException e) {
+ Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ + app.processName, e);
+ app.pendingStart = false;
+ mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+ false, false, true, false, false, app.userId, "start failure");
+ }
+ return app.pid > 0;
+ }
+ }
+
+ private Process.ProcessStartResult startProcess(String hostingType, String entryPoint,
+ ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+ String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+ long startTime) {
+ try {
+ final String[] packageNames = mService.mContext.getPackageManager()
+ .getPackagesForUid(uid);
+ final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
+ .getVisibleVolumesForUser(UserHandle.getUserId(uid));
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
+ app.processName);
+ checkSlow(startTime, "startProcess: asking zygote to start proc");
+ final Process.ProcessStartResult startResult;
+ if (hostingType.equals("webview_service")) {
+ startResult = startWebView(entryPoint,
+ app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+ app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+ app.info.dataDir, null, app.info.packageName,
+ packageNames, visibleVolIds,
+ new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+ } else {
+ startResult = Process.start(entryPoint,
+ app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+ app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+ app.info.dataDir, invokeWith, app.info.packageName,
+ packageNames, visibleVolIds,
+ new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+ }
+ checkSlow(startTime, "startProcess: returned from zygote!");
+ return startResult;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ @GuardedBy("mService")
+ final void startProcessLocked(ProcessRecord app,
+ String hostingType, String hostingNameStr) {
+ startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
+ }
+
+ @GuardedBy("mService")
+ final boolean startProcessLocked(ProcessRecord app,
+ String hostingType, String hostingNameStr, String abiOverride) {
+ return startProcessLocked(app, hostingType, hostingNameStr,
+ false /* disableHiddenApiChecks */, false /* mountExtStorageFull */, abiOverride);
+ }
+
+ @GuardedBy("mService")
+ final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+ boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
+ boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
+ String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
+ long startTime = SystemClock.elapsedRealtime();
+ ProcessRecord app;
+ if (!isolated) {
+ app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
+ checkSlow(startTime, "startProcess: after getProcessRecord");
+
+ if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
+ // If we are in the background, then check to see if this process
+ // is bad. If so, we will just silently fail.
+ if (mService.mAppErrors.isBadProcessLocked(info)) {
+ if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+ + "/" + info.processName);
+ return null;
+ }
+ } else {
+ // When the user is explicitly starting a process, then clear its
+ // crash count so that we won't make it bad until they see at
+ // least one crash dialog again, and make the process good again
+ // if it had been bad.
+ if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ + "/" + info.processName);
+ mService.mAppErrors.resetProcessCrashTimeLocked(info);
+ if (mService.mAppErrors.isBadProcessLocked(info)) {
+ EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
+ UserHandle.getUserId(info.uid), info.uid,
+ info.processName);
+ mService.mAppErrors.clearBadProcessLocked(info);
+ if (app != null) {
+ app.bad = false;
+ }
+ }
+ }
+ } else {
+ // If this is an isolated process, it can't re-use an existing process.
+ app = null;
+ }
+
+ // We don't have to do anything more if:
+ // (1) There is an existing application record; and
+ // (2) The caller doesn't think it is dead, OR there is no thread
+ // object attached to it so we know it couldn't have crashed; and
+ // (3) There is a pid assigned to it, so it is either starting or
+ // already running.
+ if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
+ + " app=" + app + " knownToBeDead=" + knownToBeDead
+ + " thread=" + (app != null ? app.thread : null)
+ + " pid=" + (app != null ? app.pid : -1));
+ if (app != null && app.pid > 0) {
+ if ((!knownToBeDead && !app.killed) || app.thread == null) {
+ // We already have the app running, or are waiting for it to
+ // come up (we have a pid but not yet its thread), so keep it.
+ if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
+ // If this is a new package in the process, add the package to the list
+ app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
+ checkSlow(startTime, "startProcess: done, added package to proc");
+ return app;
+ }
+
+ // An application record is attached to a previous process,
+ // clean it up now.
+ if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
+ checkSlow(startTime, "startProcess: bad proc running, killing");
+ ProcessList.killProcessGroup(app.uid, app.pid);
+ mService.handleAppDiedLocked(app, true, true);
+ checkSlow(startTime, "startProcess: done killing old proc");
+ }
+
+ String hostingNameStr = hostingName != null
+ ? hostingName.flattenToShortString() : null;
+
+ if (app == null) {
+ checkSlow(startTime, "startProcess: creating new process record");
+ app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+ if (app == null) {
+ Slog.w(TAG, "Failed making new process record for "
+ + processName + "/" + info.uid + " isolated=" + isolated);
+ return null;
+ }
+ app.crashHandler = crashHandler;
+ app.isolatedEntryPoint = entryPoint;
+ app.isolatedEntryPointArgs = entryPointArgs;
+ checkSlow(startTime, "startProcess: done creating new process record");
+ } else {
+ // If this is a new package in the process, add the package to the list
+ app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
+ checkSlow(startTime, "startProcess: added package to existing proc");
+ }
+
+ // If the system is not ready yet, then hold off on starting this
+ // process until it is.
+ if (!mService.mProcessesReady
+ && !mService.isAllowedWhileBooting(info)
+ && !allowWhileBooting) {
+ if (!mService.mProcessesOnHold.contains(app)) {
+ mService.mProcessesOnHold.add(app);
+ }
+ if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
+ "System not ready, putting on hold: " + app);
+ checkSlow(startTime, "startProcess: returning with proc on hold");
+ return app;
+ }
+
+ checkSlow(startTime, "startProcess: stepping in to startProcess");
+ final boolean success = startProcessLocked(app, hostingType, hostingNameStr,
+ abiOverride);
+ checkSlow(startTime, "startProcess: done starting proc!");
+ return success ? app : null;
+ }
+
+ @GuardedBy("mService")
+ private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
+ StringBuilder sb = null;
+ if (app.killedByAm) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("killedByAm=true;");
+ }
+ if (mProcessNames.get(app.processName, app.uid) != app) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("No entry in mProcessNames;");
+ }
+ if (!app.pendingStart) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("pendingStart=false;");
+ }
+ if (app.startSeq > expectedStartSeq) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+ }
+ return sb == null ? null : sb.toString();
+ }
+
+ @GuardedBy("mService")
+ private boolean handleProcessStartedLocked(ProcessRecord pending,
+ Process.ProcessStartResult startResult, long expectedStartSeq) {
+ // Indicates that this process start has been taken care of.
+ if (mPendingStarts.get(expectedStartSeq) == null) {
+ if (pending.pid == startResult.pid) {
+ pending.setUsingWrapper(startResult.usingWrapper);
+ // TODO: Update already existing clients of usingWrapper
+ }
+ return false;
+ }
+ return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
+ expectedStartSeq, false);
+ }
+
+ @GuardedBy("mService")
+ boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
+ long expectedStartSeq, boolean procAttached) {
+ mPendingStarts.remove(expectedStartSeq);
+ final String reason = isProcStartValidLocked(app, expectedStartSeq);
+ if (reason != null) {
+ Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
+ pid
+ + ", " + reason);
+ app.pendingStart = false;
+ killProcessQuiet(pid);
+ Process.killProcessGroup(app.uid, app.pid);
+ return false;
+ }
+ mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
+ checkSlow(app.startTime, "startProcess: done updating battery stats");
+
+ EventLog.writeEvent(EventLogTags.AM_PROC_START,
+ UserHandle.getUserId(app.startUid), pid, app.startUid,
+ app.processName, app.hostingType,
+ app.hostingNameStr != null ? app.hostingNameStr : "");
+
+ try {
+ AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+ app.seInfo, app.info.sourceDir, pid);
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+
+ if (app.isPersistent()) {
+ Watchdog.getInstance().processStarted(app.processName, pid);
+ }
+
+ checkSlow(app.startTime, "startProcess: building log message");
+ StringBuilder buf = mStringBuilder;
+ buf.setLength(0);
+ buf.append("Start proc ");
+ buf.append(pid);
+ buf.append(':');
+ buf.append(app.processName);
+ buf.append('/');
+ UserHandle.formatUid(buf, app.startUid);
+ if (app.isolatedEntryPoint != null) {
+ buf.append(" [");
+ buf.append(app.isolatedEntryPoint);
+ buf.append("]");
+ }
+ buf.append(" for ");
+ buf.append(app.hostingType);
+ if (app.hostingNameStr != null) {
+ buf.append(" ");
+ buf.append(app.hostingNameStr);
+ }
+ mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
+ app.setPid(pid);
+ app.setUsingWrapper(usingWrapper);
+ app.pendingStart = false;
+ checkSlow(app.startTime, "startProcess: starting to update pids map");
+ ProcessRecord oldApp;
+ synchronized (mService.mPidsSelfLocked) {
+ oldApp = mService.mPidsSelfLocked.get(pid);
+ }
+ // If there is already an app occupying that pid that hasn't been cleaned up
+ if (oldApp != null && !app.isolated) {
+ // Clean up anything relating to this pid first
+ Slog.w(TAG, "Reusing pid " + pid
+ + " while app is still mapped to it");
+ mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+ true /*replacingPid*/);
+ }
+ synchronized (mService.mPidsSelfLocked) {
+ mService.mPidsSelfLocked.put(pid, app);
+ if (!procAttached) {
+ Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+ msg.obj = app;
+ mService.mHandler.sendMessageDelayed(msg, usingWrapper
+ ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+ }
+ }
+ checkSlow(app.startTime, "startProcess: done updating pids map");
+ return true;
+ }
+
+ final void removeLruProcessLocked(ProcessRecord app) {
+ int lrui = mLruProcesses.lastIndexOf(app);
+ if (lrui >= 0) {
+ if (!app.killed) {
+ if (app.isPersistent()) {
+ Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
+ } else {
+ Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
+ if (app.pid > 0) {
+ killProcessQuiet(app.pid);
+ ProcessList.killProcessGroup(app.uid, app.pid);
+ } else {
+ app.pendingStart = false;
+ }
+ }
+ }
+ if (lrui <= mLruProcessActivityStart) {
+ mLruProcessActivityStart--;
+ }
+ if (lrui <= mLruProcessServiceStart) {
+ mLruProcessServiceStart--;
+ }
+ mLruProcesses.remove(lrui);
+ }
+ }
+
+ void killAllBackgroundProcessesLocked() {
+ final ArrayList<ProcessRecord> procs = new ArrayList<>();
+ final int NP = mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final int NA = apps.size();
+ for (int ia = 0; ia < NA; ia++) {
+ final ProcessRecord app = apps.valueAt(ia);
+ if (app.isPersistent()) {
+ // We don't kill persistent processes.
+ continue;
+ }
+ if (app.removed) {
+ procs.add(app);
+ } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ app.removed = true;
+ procs.add(app);
+ }
+ }
+ }
+
+ final int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ removeProcessLocked(procs.get(i), false, true, "kill all background");
+ }
+ }
+
+ @GuardedBy("mService")
+ final boolean killPackageProcessesLocked(String packageName, int appId,
+ int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+ boolean doit, boolean evenPersistent, String reason) {
+ ArrayList<ProcessRecord> procs = new ArrayList<>();
+
+ // Remove all processes this package may have touched: all with the
+ // same UID (except for the system or root user), and all whose name
+ // matches the package name.
+ final int NP = mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final int NA = apps.size();
+ for (int ia = 0; ia < NA; ia++) {
+ ProcessRecord app = apps.valueAt(ia);
+ if (app.isPersistent() && !evenPersistent) {
+ // we don't kill persistent processes
+ continue;
+ }
+ if (app.removed) {
+ if (doit) {
+ procs.add(app);
+ }
+ continue;
+ }
+
+ // Skip process if it doesn't meet our oom adj requirement.
+ if (app.setAdj < minOomAdj) {
+ continue;
+ }
+
+ // If no package is specified, we call all processes under the
+ // give user id.
+ if (packageName == null) {
+ if (userId != UserHandle.USER_ALL && app.userId != userId) {
+ continue;
+ }
+ if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
+ continue;
+ }
+ // Package has been specified, we want to hit all processes
+ // that match it. We need to qualify this by the processes
+ // that are running under the specified app and user ID.
+ } else {
+ final boolean isDep = app.pkgDeps != null
+ && app.pkgDeps.contains(packageName);
+ if (!isDep && UserHandle.getAppId(app.uid) != appId) {
+ continue;
+ }
+ if (userId != UserHandle.USER_ALL && app.userId != userId) {
+ continue;
+ }
+ if (!app.pkgList.containsKey(packageName) && !isDep) {
+ continue;
+ }
+ }
+
+ // Process has passed all conditions, kill it!
+ if (!doit) {
+ return true;
+ }
+ app.removed = true;
+ procs.add(app);
+ }
+ }
+
+ int N = procs.size();
+ for (int i=0; i<N; i++) {
+ removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
+ }
+ mService.updateOomAdjLocked();
+ return N > 0;
+ }
+ @GuardedBy("mService")
+ boolean removeProcessLocked(ProcessRecord app,
+ boolean callerWillRestart, boolean allowRestart, String reason) {
+ final String name = app.processName;
+ final int uid = app.uid;
+ if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
+ "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
+
+ ProcessRecord old = mProcessNames.get(name, uid);
+ if (old != app) {
+ // This process is no longer active, so nothing to do.
+ Slog.w(TAG, "Ignoring remove of inactive process: " + app);
+ return false;
+ }
+ removeProcessNameLocked(name, uid);
+ mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+
+ boolean needRestart = false;
+ if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
+ .pendingStart)) {
+ int pid = app.pid;
+ if (pid > 0) {
+ synchronized (mService.mPidsSelfLocked) {
+ mService.mPidsSelfLocked.remove(pid);
+ mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+ }
+ mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+ if (app.isolated) {
+ mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
+ }
+ }
+ boolean willRestart = false;
+ if (app.isPersistent() && !app.isolated) {
+ if (!callerWillRestart) {
+ willRestart = true;
+ } else {
+ needRestart = true;
+ }
+ }
+ app.kill(reason, true);
+ mService.handleAppDiedLocked(app, willRestart, allowRestart);
+ if (willRestart) {
+ removeLruProcessLocked(app);
+ mService.addAppLocked(app.info, null, false, null /* ABI override */);
+ }
+ } else {
+ mRemovedProcesses.add(app);
+ }
+
+ return needRestart;
+ }
+
+ @GuardedBy("mService")
+ final void addProcessNameLocked(ProcessRecord proc) {
+ // We shouldn't already have a process under this name, but just in case we
+ // need to clean up whatever may be there now.
+ ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+ if (old == proc && proc.isPersistent()) {
+ // We are re-adding a persistent process. Whatevs! Just leave it there.
+ Slog.w(TAG, "Re-adding persistent process " + proc);
+ } else if (old != null) {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
+ UidRecord uidRec = mService.mActiveUids.get(proc.uid);
+ if (uidRec == null) {
+ uidRec = new UidRecord(proc.uid);
+ // This is the first appearance of the uid, report it now!
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "Creating new process uid: " + uidRec);
+ if (Arrays.binarySearch(mService.mDeviceIdleTempWhitelist,
+ UserHandle.getAppId(proc.uid)) >= 0
+ || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
+ uidRec.setWhitelist = uidRec.curWhitelist = true;
+ }
+ uidRec.updateHasInternetPermission();
+ mService.mActiveUids.put(proc.uid, uidRec);
+ EventLogTags.writeAmUidRunning(uidRec.uid);
+ mService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ }
+ proc.uidRecord = uidRec;
+
+ // Reset render thread tid if it was already set, so new process can set it again.
+ proc.renderThreadTid = 0;
+ uidRec.numProcs++;
+ mProcessNames.put(proc.processName, proc.uid, proc);
+ if (proc.isolated) {
+ mIsolatedProcesses.put(proc.uid, proc);
+ }
+ }
+
+ @GuardedBy("mService")
+ final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
+ boolean isolated, int isolatedUid) {
+ String proc = customProcess != null ? customProcess : info.processName;
+ final int userId = UserHandle.getUserId(info.uid);
+ int uid = info.uid;
+ if (isolated) {
+ if (isolatedUid == 0) {
+ int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
+ while (true) {
+ if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
+ || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
+ mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
+ }
+ uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
+ mNextIsolatedProcessUid++;
+ if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+ // No process for this uid, use it.
+ break;
+ }
+ stepsLeft--;
+ if (stepsLeft <= 0) {
+ return null;
+ }
+ }
+ } else {
+ // Special case for startIsolatedProcess (internal only), where
+ // the uid of the isolated process is specified by the caller.
+ uid = isolatedUid;
+ }
+ mService.getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
+
+ // Register the isolated UID with this application so BatteryStats knows to
+ // attribute resource usage to the application.
+ //
+ // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
+ // about the process state of the isolated UID *before* it is registered with the
+ // owning application.
+ mService.mBatteryStatsService.addIsolatedUid(uid, info.uid);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
+ StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
+ }
+ final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
+ mService.getGlobalConfiguration());
+
+ if (!mService.mBooted && !mService.mBooting
+ && userId == UserHandle.USER_SYSTEM
+ && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+ // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+ r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+ r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ r.setPersistent(true);
+ r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+ }
+ if (isolated && isolatedUid != 0) {
+ // Special case for startIsolatedProcess (internal only) - assume the process
+ // is required by the system server to prevent it being killed.
+ r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+ }
+ addProcessNameLocked(r);
+ return r;
+ }
+
+ @GuardedBy("mService")
+ final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+ return removeProcessNameLocked(name, uid, null);
+ }
+
+ @GuardedBy("mService")
+ final ProcessRecord removeProcessNameLocked(final String name, final int uid,
+ final ProcessRecord expecting) {
+ ProcessRecord old = mProcessNames.get(name, uid);
+ // Only actually remove when the currently recorded value matches the
+ // record that we expected; if it doesn't match then we raced with a
+ // newly created process and we don't want to destroy the new one.
+ if ((expecting == null) || (old == expecting)) {
+ mProcessNames.remove(name, uid);
+ }
+ if (old != null && old.uidRecord != null) {
+ old.uidRecord.numProcs--;
+ if (old.uidRecord.numProcs == 0) {
+ // No more processes using this uid, tell clients it is gone.
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "No more processes in " + old.uidRecord);
+ mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+ EventLogTags.writeAmUidStopped(uid);
+ mService.mActiveUids.remove(uid);
+ mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
+ }
+ old.uidRecord = null;
+ }
+ mIsolatedProcesses.remove(uid);
+ return old;
+ }
+
+ /** Call setCoreSettings on all LRU processes, with the new settings. */
+ @GuardedBy("mService")
+ void updateCoreSettingsLocked(Bundle settings) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord processRecord = mLruProcesses.get(i);
+ try {
+ if (processRecord.thread != null) {
+ processRecord.thread.setCoreSettings(settings);
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+
+ /**
+ * Kill all background processes except for ones with targetSdk lower than minTargetSdk and
+ * procstate lower than maxProcState.
+ * @param minTargetSdk
+ * @param maxProcState
+ */
+ @GuardedBy("mService")
+ void killAllBackgroundProcessesExceptLocked(int minTargetSdk, int maxProcState) {
+ final ArrayList<ProcessRecord> procs = new ArrayList<>();
+ final int NP = mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final int NA = apps.size();
+ for (int ia = 0; ia < NA; ia++) {
+ final ProcessRecord app = apps.valueAt(ia);
+ if (app.removed) {
+ procs.add(app);
+ } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
+ && (maxProcState < 0 || app.setProcState > maxProcState)) {
+ app.removed = true;
+ procs.add(app);
+ }
+ }
+ }
+
+ final int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ removeProcessLocked(procs.get(i), false, true, "kill all background except");
+ }
+ }
+
+ /**
+ * Call updateTimePrefs on all LRU processes
+ * @param timePref The time pref to pass to each process
+ */
+ @GuardedBy("mService")
+ void updateAllTimePrefsLocked(int timePref) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null) {
+ try {
+ r.thread.updateTimePrefs(timePref);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to update preferences for: "
+ + r.info.processName);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void setAllHttpProxyLocked(String host, String port, String exclList, Uri pacFileUrl) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ // Don't dispatch to isolated processes as they can't access
+ // ConnectivityManager and don't have network privileges anyway.
+ if (r.thread != null && !r.isolated) {
+ try {
+ r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to update http proxy for: " +
+ r.info.processName);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void clearAllDnsCacheLocked() {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null) {
+ try {
+ r.thread.clearDnsCache();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void handleAllTrustStorageUpdateLocked() {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null) {
+ try {
+ r.thread.handleTrustStorageUpdate();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to handle trust storage update for: " +
+ r.info.processName);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
+ String what, Object obj, ProcessRecord srcApp) {
+ app.lastActivityTime = now;
+
+ if (app.hasActivitiesOrRecentTasks()) {
+ // Don't want to touch dependent processes that are hosting activities.
+ return index;
+ }
+
+ int lrui = mLruProcesses.lastIndexOf(app);
+ if (lrui < 0) {
+ Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+ + what + " " + obj + " from " + srcApp);
+ return index;
+ }
+
+ if (lrui >= index) {
+ // Don't want to cause this to move dependent processes *back* in the
+ // list as if they were less frequently used.
+ return index;
+ }
+
+ if (lrui >= mLruProcessActivityStart) {
+ // Don't want to touch dependent processes that are hosting activities.
+ return index;
+ }
+
+ mLruProcesses.remove(lrui);
+ if (index > 0) {
+ index--;
+ }
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ + " in LRU list: " + app);
+ mLruProcesses.add(index, app);
+ return index;
+ }
+
+ final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+ ProcessRecord client) {
+ final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
+ || app.treatLikeActivity;
+ final boolean hasService = false; // not impl yet. app.services.size() > 0;
+ if (!activityChange && hasActivity) {
+ // The process has activities, so we are only allowing activity-based adjustments
+ // to move it. It should be kept in the front of the list with other
+ // processes that have activities, and we don't want those to change their
+ // order except due to activity operations.
+ return;
+ }
+
+ mLruSeq++;
+ final long now = SystemClock.uptimeMillis();
+ app.lastActivityTime = now;
+
+ // First a quick reject: if the app is already at the position we will
+ // put it, then there is nothing to do.
+ if (hasActivity) {
+ final int N = mLruProcesses.size();
+ if (N > 0 && mLruProcesses.get(N - 1) == app) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
+ return;
+ }
+ } else {
+ if (mLruProcessServiceStart > 0
+ && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
+ return;
+ }
+ }
+
+ int lrui = mLruProcesses.lastIndexOf(app);
+
+ if (app.isPersistent() && lrui >= 0) {
+ // We don't care about the position of persistent processes, as long as
+ // they are in the list.
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
+ return;
+ }
+
+ /* In progress: compute new position first, so we can avoid doing work
+ if the process is not actually going to move. Not yet working.
+ int addIndex;
+ int nextIndex;
+ boolean inActivity = false, inService = false;
+ if (hasActivity) {
+ // Process has activities, put it at the very tipsy-top.
+ addIndex = mLruProcesses.size();
+ nextIndex = mLruProcessServiceStart;
+ inActivity = true;
+ } else if (hasService) {
+ // Process has services, put it at the top of the service list.
+ addIndex = mLruProcessActivityStart;
+ nextIndex = mLruProcessServiceStart;
+ inActivity = true;
+ inService = true;
+ } else {
+ // Process not otherwise of interest, it goes to the top of the non-service area.
+ addIndex = mLruProcessServiceStart;
+ if (client != null) {
+ int clientIndex = mLruProcesses.lastIndexOf(client);
+ if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
+ + app);
+ if (clientIndex >= 0 && addIndex > clientIndex) {
+ addIndex = clientIndex;
+ }
+ }
+ nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
+ }
+
+ Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
+ + mLruProcessActivityStart + "): " + app);
+ */
+
+ if (lrui >= 0) {
+ if (lrui < mLruProcessActivityStart) {
+ mLruProcessActivityStart--;
+ }
+ if (lrui < mLruProcessServiceStart) {
+ mLruProcessServiceStart--;
+ }
+ /*
+ if (addIndex > lrui) {
+ addIndex--;
+ }
+ if (nextIndex > lrui) {
+ nextIndex--;
+ }
+ */
+ mLruProcesses.remove(lrui);
+ }
+
+ /*
+ mLruProcesses.add(addIndex, app);
+ if (inActivity) {
+ mLruProcessActivityStart++;
+ }
+ if (inService) {
+ mLruProcessActivityStart++;
+ }
+ */
+
+ int nextIndex;
+ if (hasActivity) {
+ final int N = mLruProcesses.size();
+ if ((!app.hasActivities() || app.hasRecentTasks())
+ && mLruProcessActivityStart < (N - 1)) {
+ // Process doesn't have activities, but has clients with
+ // activities... move it up, but one below the top (the top
+ // should always have a real activity).
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Adding to second-top of LRU activity list: " + app);
+ mLruProcesses.add(N - 1, app);
+ // To keep it from spamming the LRU list (by making a bunch of clients),
+ // we will push down any other entries owned by the app.
+ final int uid = app.info.uid;
+ for (int i = N - 2; i > mLruProcessActivityStart; i--) {
+ ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid == uid) {
+ // We want to push this one down the list. If the process after
+ // it is for the same uid, however, don't do so, because we don't
+ // want them internally to be re-ordered.
+ if (mLruProcesses.get(i - 1).info.uid != uid) {
+ if (DEBUG_LRU) Slog.d(TAG_LRU,
+ "Pushing uid " + uid + " swapping at " + i + ": "
+ + mLruProcesses.get(i) + " : "
+ + mLruProcesses.get(i - 1));
+ ProcessRecord tmp = mLruProcesses.get(i);
+ mLruProcesses.set(i, mLruProcesses.get(i - 1));
+ mLruProcesses.set(i - 1, tmp);
+ i--;
+ }
+ } else {
+ // A gap, we can stop here.
+ break;
+ }
+ }
+ } else {
+ // Process has activities, put it at the very tipsy-top.
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
+ mLruProcesses.add(app);
+ }
+ nextIndex = mLruProcessServiceStart;
+ } else if (hasService) {
+ // Process has services, put it at the top of the service list.
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
+ mLruProcesses.add(mLruProcessActivityStart, app);
+ nextIndex = mLruProcessServiceStart;
+ mLruProcessActivityStart++;
+ } else {
+ // Process not otherwise of interest, it goes to the top of the non-service area.
+ int index = mLruProcessServiceStart;
+ if (client != null) {
+ // If there is a client, don't allow the process to be moved up higher
+ // in the list than that client.
+ int clientIndex = mLruProcesses.lastIndexOf(client);
+ if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
+ + " when updating " + app);
+ if (clientIndex <= lrui) {
+ // Don't allow the client index restriction to push it down farther in the
+ // list than it already is.
+ clientIndex = lrui;
+ }
+ if (clientIndex >= 0 && index > clientIndex) {
+ index = clientIndex;
+ }
+ }
+ if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
+ mLruProcesses.add(index, app);
+ nextIndex = index - 1;
+ mLruProcessActivityStart++;
+ mLruProcessServiceStart++;
+ }
+
+ // If the app is currently using a content provider or service,
+ // bump those processes as well.
+ for (int j = app.connections.size() - 1; j >= 0; j--) {
+ ConnectionRecord cr = app.connections.valueAt(j);
+ if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
+ && cr.binding.service.app != null
+ && cr.binding.service.app.lruSeq != mLruSeq
+ && !cr.binding.service.app.isPersistent()) {
+ nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextIndex,
+ "service connection", cr, app);
+ }
+ }
+ for (int j = app.conProviders.size() - 1; j >= 0; j--) {
+ ContentProviderRecord cpr = app.conProviders.get(j).provider;
+ if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
+ nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+ "provider reference", cpr, app);
+ }
+ }
+ }
+
+ final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
+ final IBinder threadBinder = thread.asBinder();
+ // Find the application record.
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord rec = mLruProcesses.get(i);
+ if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
+ return rec;
+ }
+ }
+ return null;
+ }
+
+ boolean haveBackgroundProcessLocked() {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord rec = mLruProcesses.get(i);
+ if (rec.thread != null
+ && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static int procStateToImportance(int procState, int memAdj,
+ ActivityManager.RunningAppProcessInfo currApp,
+ int clientTargetSdk) {
+ int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
+ procState, clientTargetSdk);
+ if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+ currApp.lru = memAdj;
+ } else {
+ currApp.lru = 0;
+ }
+ return imp;
+ }
+
+ @GuardedBy("mService")
+ void fillInProcMemInfoLocked(ProcessRecord app,
+ ActivityManager.RunningAppProcessInfo outInfo,
+ int clientTargetSdk) {
+ outInfo.pid = app.pid;
+ outInfo.uid = app.info.uid;
+ if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+ outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
+ }
+ if (app.isPersistent()) {
+ outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
+ }
+ if (app.hasActivities()) {
+ outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
+ }
+ outInfo.lastTrimLevel = app.trimMemoryLevel;
+ int adj = app.curAdj;
+ int procState = app.getCurProcState();
+ outInfo.importance = procStateToImportance(procState, adj, outInfo,
+ clientTargetSdk);
+ outInfo.importanceReasonCode = app.adjTypeCode;
+ outInfo.processState = app.getCurProcState();
+ outInfo.isFocused = (app == mService.getTopAppLocked());
+ outInfo.lastActivityTime = app.lastActivityTime;
+ }
+
+ @GuardedBy("mService")
+ List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers,
+ int userId, boolean allUids, int callingUid, int clientTargetSdk) {
+ // Lazy instantiation of list
+ List<ActivityManager.RunningAppProcessInfo> runList = null;
+
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord app = mLruProcesses.get(i);
+ if ((!allUsers && app.userId != userId)
+ || (!allUids && app.uid != callingUid)) {
+ continue;
+ }
+ if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
+ // Generate process state info for running application
+ ActivityManager.RunningAppProcessInfo currApp =
+ new ActivityManager.RunningAppProcessInfo(app.processName,
+ app.pid, app.getPackageList());
+ fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
+ if (app.adjSource instanceof ProcessRecord) {
+ currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+ currApp.importanceReasonImportance =
+ ActivityManager.RunningAppProcessInfo.procStateToImportance(
+ app.adjSourceProcState);
+ } else if (app.adjSource instanceof ActivityRecord) {
+ ActivityRecord r = (ActivityRecord)app.adjSource;
+ if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+ }
+ if (app.adjTarget instanceof ComponentName) {
+ currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+ }
+ //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
+ // + " lru=" + currApp.lru);
+ if (runList == null) {
+ runList = new ArrayList<>();
+ }
+ runList.add(currApp);
+ }
+ }
+ return runList;
+ }
+
+ @GuardedBy("mService")
+ int getLruSizeLocked() {
+ return mLruProcesses.size();
+ }
+
+ @GuardedBy("mService")
+ void dumpLruListHeaderLocked(PrintWriter pw) {
+ pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
+ pw.print(" total, non-act at ");
+ pw.print(mLruProcesses.size() - mLruProcessActivityStart);
+ pw.print(", non-svc at ");
+ pw.print(mLruProcesses.size() - mLruProcessServiceStart);
+ pw.println("):");
+ }
+
+ @GuardedBy("mService")
+ ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
+ ArrayList<ProcessRecord> procs;
+ if (args != null && args.length > start
+ && args[start].charAt(0) != '-') {
+ procs = new ArrayList<ProcessRecord>();
+ int pid = -1;
+ try {
+ pid = Integer.parseInt(args[start]);
+ } catch (NumberFormatException e) {
+ }
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord proc = mLruProcesses.get(i);
+ if (proc.pid > 0 && proc.pid == pid) {
+ procs.add(proc);
+ } else if (allPkgs && proc.pkgList != null
+ && proc.pkgList.containsKey(args[start])) {
+ procs.add(proc);
+ } else if (proc.processName.equals(args[start])) {
+ procs.add(proc);
+ }
+ }
+ if (procs.size() <= 0) {
+ return null;
+ }
+ } else {
+ procs = new ArrayList<ProcessRecord>(mLruProcesses);
+ }
+ return procs;
+ }
+
+ @GuardedBy("mService")
+ void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId,
+ boolean updateFrameworkRes) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mLruProcesses.get(i);
+ if (app.thread == null) {
+ continue;
+ }
+
+ if (userId != UserHandle.USER_ALL && app.userId != userId) {
+ continue;
+ }
+
+ final int packageCount = app.pkgList.size();
+ for (int j = 0; j < packageCount; j++) {
+ final String packageName = app.pkgList.keyAt(j);
+ if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
+ try {
+ final ApplicationInfo ai = AppGlobals.getPackageManager()
+ .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
+ if (ai != null) {
+ app.thread.scheduleApplicationInfoChanged(ai);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
+ packageName, app));
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
+ try {
+ r.thread.dispatchPackageBroadcast(cmd, packages);
+ } catch (RemoteException ex) {
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 667d3fa..fa7a08b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -18,12 +18,17 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.MY_PID;
import android.app.ActivityManager;
+import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -32,16 +37,19 @@
import android.os.Binder;
import android.os.Debug;
import android.os.IBinder;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -49,7 +57,11 @@
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.server.Watchdog;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -128,7 +140,7 @@
long lastCachedPss; // Last computed pss when in cached state.
long lastCachedSwapPss; // Last computed SwapPss when in cached state.
int maxAdj; // Maximum OOM adjustment for this process
- int curRawAdj; // Current OOM unlimited adjustment for this process
+ private int mCurRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
int curAdj; // Current OOM adjustment for this process
int setAdj; // Last set OOM adjustment for this process
@@ -136,7 +148,7 @@
private int mCurSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
int trimMemoryLevel; // Last selected memory trimming level
- int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
+ private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
@@ -146,19 +158,19 @@
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
- boolean hasClientActivities; // Are there any client services with activities?
+ private boolean mHasClientActivities; // Are there any client services with activities?
boolean hasStartedServices; // Are there any started services running in this process?
private boolean mHasForegroundServices; // Running any services that are foreground?
- boolean foregroundActivities; // Running any activities that are foreground?
+ private boolean mHasForegroundActivities; // Running any activities that are foreground?
boolean repForegroundActivities; // Last reported foreground activities.
boolean systemNoUi; // This is a system process, but not currently showing UI.
boolean hasShownUi; // Has UI been shown in this process since it was started?
- boolean hasTopUi; // Is this process currently showing a non-activity UI that the user
+ private boolean mHasTopUi; // Is this process currently showing a non-activity UI that the user
// is interacting with? E.g. The status bar when it is expanded, but
// not when it is minimized. When true the
// process will be set to use the ProcessList#SCHED_GROUP_TOP_APP
// scheduling group to boost performance.
- boolean hasOverlayUi; // Is the process currently showing a non-activity UI that
+ private boolean mHasOverlayUi; // Is the process currently showing a non-activity UI that
// overlays on-top of activity UIs on screen. E.g. display a window
// of type
// android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
@@ -171,7 +183,7 @@
// 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?
+ private boolean mPendingUiClean; // 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
boolean bad; // True if disabled in the bad process list
@@ -180,8 +192,8 @@
boolean procStateChanged; // Keep track of whether we changed 'setAdj'.
boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
boolean unlocked; // True when proc was started in user unlocked state
- long interactionEventTime; // The time we sent the last interaction event
- long fgInteractionTime; // When we became foreground for interaction purposes
+ private long mInteractionEventTime; // The time we sent the last interaction event
+ private long mFgInteractionTime; // When we became foreground for interaction purposes
String waitingToKill; // Process is waiting to be killed when in the bg, and reason
Object forcingToImportant; // Token that is forcing this process to be important
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
@@ -194,7 +206,7 @@
// process.
private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
- long whenUnimportant; // When (uptime) the process last became unimportant
+ private long mWhenUnimportant; // When (uptime) the process last became unimportant
long lastCpuTime; // How long proc has run CPU at last check
long curCpuTime; // How long proc has run CPU most recently
long lastRequestedGc; // When we last asked the app to do a gc
@@ -362,7 +374,7 @@
pw.print(" initialIdlePss="); pw.println(initialIdlePss);
}
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
- pw.print(" curRaw="); pw.print(curRawAdj);
+ pw.print(" curRaw="); pw.print(mCurRawAdj);
pw.print(" setRaw="); pw.print(setRawAdj);
pw.print(" cur="); pw.print(curAdj);
pw.print(" set="); pw.println(setAdj);
@@ -370,38 +382,38 @@
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
- pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
+ pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
pw.print(" mRepProcState="); pw.print(mRepProcState);
pw.print(" pssProcState="); pw.print(pssProcState);
pw.print(" setProcState="); pw.print(setProcState);
pw.print(" lastStateTime=");
TimeUtils.formatDuration(lastStateTime, nowUptime, pw);
pw.println();
- if (hasShownUi || pendingUiClean || hasAboveClient || treatLikeActivity) {
+ if (hasShownUi || mPendingUiClean || hasAboveClient || treatLikeActivity) {
pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
- pw.print(" pendingUiClean="); pw.print(pendingUiClean);
+ pw.print(" pendingUiClean="); pw.print(mPendingUiClean);
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
- if (hasTopUi || hasOverlayUi || runningRemoteAnimation) {
- pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi);
- pw.print(" hasOverlayUi="); pw.print(hasOverlayUi);
+ if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
+ pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
+ pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
}
if (mHasForegroundServices || forcingToImportant != null) {
pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
pw.print(" forcingToImportant="); pw.println(forcingToImportant);
}
- if (reportedInteraction || fgInteractionTime != 0) {
+ if (reportedInteraction || mFgInteractionTime != 0) {
pw.print(prefix); pw.print("reportedInteraction=");
pw.print(reportedInteraction);
- if (interactionEventTime != 0) {
+ if (mInteractionEventTime != 0) {
pw.print(" time=");
- TimeUtils.formatDuration(interactionEventTime, SystemClock.elapsedRealtime(), pw);
+ TimeUtils.formatDuration(mInteractionEventTime, SystemClock.elapsedRealtime(), pw);
}
- if (fgInteractionTime != 0) {
+ if (mFgInteractionTime != 0) {
pw.print(" fgInteractionTime=");
- TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw);
+ TimeUtils.formatDuration(mFgInteractionTime, SystemClock.elapsedRealtime(), pw);
}
pw.println();
}
@@ -409,9 +421,9 @@
pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
pw.print(" removed="); pw.println(removed);
}
- if (hasClientActivities || foregroundActivities || repForegroundActivities) {
- pw.print(prefix); pw.print("hasClientActivities="); pw.print(hasClientActivities);
- pw.print(" foregroundActivities="); pw.print(foregroundActivities);
+ if (mHasClientActivities || mHasForegroundActivities || repForegroundActivities) {
+ pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities);
+ pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
}
if (lastProviderTime > 0) {
@@ -438,7 +450,7 @@
TimeUtils.formatDuration(curCpuTime - lastCpuTime, pw);
}
pw.print(" whenUnimportant=");
- TimeUtils.formatDuration(whenUnimportant - nowUptime, pw);
+ TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
pw.println();
}
pw.print(prefix); pw.print("lastRequestedGc=");
@@ -531,7 +543,7 @@
userId = UserHandle.getUserId(_uid);
processName = _processName;
maxAdj = ProcessList.UNKNOWN_ADJ;
- curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
+ mCurRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
mPersistent = false;
removed = false;
@@ -723,7 +735,7 @@
if (pid > 0) {
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid);
- ActivityManagerService.killProcessGroup(uid, pid);
+ ProcessList.killProcessGroup(uid, pid);
} else {
pendingStart = false;
}
@@ -735,6 +747,7 @@
}
}
+ @Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(ProcessRecordProto.PID, pid);
@@ -825,6 +838,13 @@
return null;
}
+ @Override
+ public void addPackage(String pkg, long versionCode) {
+ synchronized (mService) {
+ addPackage(pkg, versionCode, mService.mProcessStats);
+ }
+ }
+
/*
* Return true if package has been added false if not
*/
@@ -857,7 +877,8 @@
public void forceProcessStateUpTo(int newState) {
if (mRepProcState > newState) {
- curProcState = mRepProcState = newState;
+ mRepProcState = newState;
+ setCurProcState(newState);
for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
uid, processName, pkgList.keyAt(ipkg),
@@ -931,6 +952,15 @@
return mCurSchedGroup;
}
+ void setCurProcState(int curProcState) {
+ mCurProcState = curProcState;
+ mWindowProcessController.setCurrentProcState(mCurProcState);
+ }
+
+ int getCurProcState() {
+ return mCurProcState;
+ }
+
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
@@ -991,6 +1021,69 @@
return mHasForegroundServices;
}
+ void setHasForegroundActivities(boolean hasForegroundActivities) {
+ mHasForegroundActivities = hasForegroundActivities;
+ mWindowProcessController.setHasForegroundActivities(hasForegroundActivities);
+ }
+
+ boolean hasForegroundActivities() {
+ return mHasForegroundActivities;
+ }
+
+ void setHasClientActivities(boolean hasClientActivities) {
+ mHasClientActivities = hasClientActivities;
+ mWindowProcessController.setHasClientActivities(hasClientActivities);
+ }
+
+ boolean hasClientActivities() {
+ return mHasClientActivities;
+ }
+
+ void setHasTopUi(boolean hasTopUi) {
+ mHasTopUi = hasTopUi;
+ mWindowProcessController.setHasTopUi(hasTopUi);
+ }
+
+ boolean hasTopUi() {
+ return mHasTopUi;
+ }
+
+ void setHasOverlayUi(boolean hasOverlayUi) {
+ mHasOverlayUi = hasOverlayUi;
+ mWindowProcessController.setHasOverlayUi(hasOverlayUi);
+ }
+
+ boolean hasOverlayUi() {
+ return mHasOverlayUi;
+ }
+
+ void setInteractionEventTime(long interactionEventTime) {
+ mInteractionEventTime = interactionEventTime;
+ mWindowProcessController.setInteractionEventTime(interactionEventTime);
+ }
+
+ long getInteractionEventTime() {
+ return mInteractionEventTime;
+ }
+
+ void setFgInteractionTime(long fgInteractionTime) {
+ mFgInteractionTime = fgInteractionTime;
+ mWindowProcessController.setFgInteractionTime(fgInteractionTime);
+ }
+
+ long getFgInteractionTime() {
+ return mFgInteractionTime;
+ }
+
+ void setWhenUnimportant(long whenUnimportant) {
+ mWhenUnimportant = whenUnimportant;
+ mWindowProcessController.setWhenUnimportant(whenUnimportant);
+ }
+
+ long getWhenUnimportant() {
+ return mWhenUnimportant;
+ }
+
void setDebugging(boolean debugging) {
mDebugging = debugging;
mWindowProcessController.setDebugging(debugging);
@@ -1018,6 +1111,15 @@
return mInstr;
}
+ void setCurRawAdj(int curRawAdj) {
+ mCurRawAdj = curRawAdj;
+ mWindowProcessController.setPerceptible(curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
+ }
+
+ int getCurRawAdj() {
+ return mCurRawAdj;
+ }
+
@Override
public void clearProfilerIfNeeded() {
synchronized (mService) {
@@ -1039,14 +1141,19 @@
@Override
public void setPendingUiClean(boolean pendingUiClean) {
synchronized (mService) {
- this.pendingUiClean = true;
+ mPendingUiClean = pendingUiClean;
+ mWindowProcessController.setPendingUiClean(pendingUiClean);
}
}
+ boolean hasPendingUiClean() {
+ return mPendingUiClean;
+ }
+
@Override
public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
synchronized (mService) {
- pendingUiClean = true;
+ setPendingUiClean(true);
forceProcessStateUpTo(newState);
}
}
@@ -1059,7 +1166,7 @@
mService.mServices.updateServiceConnectionActivitiesLocked(this);
}
if (updateLru) {
- mService.updateLruProcessLocked(this, activityChange, null);
+ mService.mProcessList.updateLruProcessLocked(this, activityChange, null);
}
if (updateOomAdj) {
mService.updateOomAdjLocked();
@@ -1078,7 +1185,315 @@
* Returns the total time (in milliseconds) spent executing in both user and system code.
* Safe to call without lock held.
*/
+ @Override
public long getCpuTime() {
return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
}
+
+ @Override
+ public void clearWaitingToKill() {
+ synchronized (mService) {
+ waitingToKill = null;
+ }
+ }
+
+ @Override
+ public ProfilerInfo onStartActivity(int topProcessState) {
+ synchronized (mService) {
+ ProfilerInfo profilerInfo = null;
+ if (mService.mProfileApp != null && mService.mProfileApp.equals(processName)) {
+ if (mService.mProfileProc == null || mService.mProfileProc == this) {
+ mService.mProfileProc = this;
+ final ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
+ if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
+ if (profilerInfoSvc.profileFd != null) {
+ try {
+ profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
+ } catch (IOException e) {
+ profilerInfoSvc.closeFd();
+ }
+ }
+
+ profilerInfo = new ProfilerInfo(profilerInfoSvc);
+ }
+ }
+ }
+
+ hasShownUi = true;
+ setPendingUiClean(true);
+ forceProcessStateUpTo(topProcessState);
+
+ return profilerInfo;
+ }
+ }
+
+ @Override
+ public void appDied() {
+ synchronized (mService) {
+ mService.appDiedLocked(this);
+ }
+ }
+
+ public long getInputDispatchingTimeout() {
+ return mWindowProcessController.getInputDispatchingTimeout();
+ }
+
+ void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
+ String parentShortComponentName, WindowProcessController parentProcess,
+ boolean aboveSystem, String annotation) {
+ ArrayList<Integer> firstPids = new ArrayList<>(5);
+ SparseArray<Boolean> lastPids = new SparseArray<>(20);
+
+ if (mService.mActivityTaskManager.mController != null) {
+ try {
+ // 0 == continue, -1 = kill process immediately
+ int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
+ processName, pid, annotation);
+ if (res < 0 && pid != MY_PID) {
+ kill("anr", true);
+ }
+ } catch (RemoteException e) {
+ mService.mActivityTaskManager.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+ }
+
+ long anrTime = SystemClock.uptimeMillis();
+ if (ActivityManagerService.MONITOR_CPU_USAGE) {
+ mService.updateCpuStatsNow();
+ }
+
+ // Unless configured otherwise, swallow ANRs in background processes & kill the process.
+ boolean showBackground = Settings.Secure.getInt(mService.mContext.getContentResolver(),
+ Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+
+ boolean isSilentANR;
+
+ synchronized (mService) {
+ // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+ if (mService.mActivityTaskManager.mShuttingDown) {
+ Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+ return;
+ } else if (isNotResponding()) {
+ Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+ return;
+ } else if (isCrashing()) {
+ Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+ return;
+ } else if (killedByAm) {
+ Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+ return;
+ } else if (killed) {
+ Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+ return;
+ }
+
+ // In case we come through here for the same app before completing
+ // this one, mark as anring now so we will bail out.
+ setNotResponding(true);
+
+ // Log the ANR to the event log.
+ EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
+ annotation);
+
+ // Dump thread traces as quickly as we can, starting with "interesting" processes.
+ firstPids.add(pid);
+
+ // Don't dump other PIDs if it's a background ANR
+ isSilentANR = !showBackground && !isInterestingForBackgroundTraces();
+ if (!isSilentANR) {
+ int parentPid = pid;
+ if (parentProcess != null && parentProcess.getPid() > 0) {
+ parentPid = parentProcess.getPid();
+ }
+ if (parentPid != pid) firstPids.add(parentPid);
+
+ if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
+
+ for (int i = mService.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mService.mProcessList.mLruProcesses.get(i);
+ if (r != null && r.thread != null) {
+ int myPid = r.pid;
+ if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
+ if (r.isPersistent()) {
+ firstPids.add(myPid);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
+ } else if (r.treatLikeActivity) {
+ firstPids.add(myPid);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
+ } else {
+ lastPids.put(myPid, Boolean.TRUE);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Log the ANR to the main log.
+ StringBuilder info = new StringBuilder();
+ info.setLength(0);
+ info.append("ANR in ").append(processName);
+ if (activityShortComponentName != null) {
+ info.append(" (").append(activityShortComponentName).append(")");
+ }
+ info.append("\n");
+ info.append("PID: ").append(pid).append("\n");
+ if (annotation != null) {
+ info.append("Reason: ").append(annotation).append("\n");
+ }
+ if (parentShortComponentName != null
+ && parentShortComponentName.equals(activityShortComponentName)) {
+ info.append("Parent: ").append(parentShortComponentName).append("\n");
+ }
+
+ ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+
+ // don't dump native PIDs for background ANRs unless it is the process of interest
+ String[] nativeProcs = null;
+ if (isSilentANR) {
+ for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
+ if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) {
+ nativeProcs = new String[] { processName };
+ break;
+ }
+ }
+ } else {
+ nativeProcs = NATIVE_STACKS_OF_INTEREST;
+ }
+
+ int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
+ ArrayList<Integer> nativePids = null;
+
+ if (pids != null) {
+ nativePids = new ArrayList<>(pids.length);
+ for (int i : pids) {
+ nativePids.add(i);
+ }
+ }
+
+ // For background ANRs, don't pass the ProcessCpuTracker to
+ // avoid spending 1/2 second collecting stats to rank lastPids.
+ File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+ (isSilentANR) ? null : processCpuTracker, (isSilentANR) ? null : lastPids,
+ nativePids);
+
+ String cpuInfo = null;
+ if (ActivityManagerService.MONITOR_CPU_USAGE) {
+ mService.updateCpuStatsNow();
+ synchronized (mService.mProcessCpuTracker) {
+ cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
+ }
+ info.append(processCpuTracker.printCurrentLoad());
+ info.append(cpuInfo);
+ }
+
+ info.append(processCpuTracker.printCurrentState(anrTime));
+
+ Slog.e(TAG, info.toString());
+ if (tracesFile == null) {
+ // There is no trace file, so dump (only) the alleged culprit's threads to the log
+ Process.sendSignal(pid, Process.SIGNAL_QUIT);
+ }
+
+ StatsLog.write(StatsLog.ANR_OCCURRED, uid, processName,
+ activityShortComponentName == null ? "unknown": activityShortComponentName,
+ annotation,
+ (this.info != null) ? (this.info.isInstantApp()
+ ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
+ : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
+ : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
+ isInterestingToUserLocked()
+ ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
+ : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND);
+ final ProcessRecord parentPr = parentProcess != null
+ ? (ProcessRecord) parentProcess.mOwner : null;
+ mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
+ parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null);
+
+ if (mService.mActivityTaskManager.mController != null) {
+ try {
+ // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
+ int res = mService.mActivityTaskManager.mController.appNotResponding(
+ processName, pid, info.toString());
+ if (res != 0) {
+ if (res < 0 && pid != MY_PID) {
+ kill("anr", true);
+ } else {
+ synchronized (mService) {
+ mService.mServices.scheduleServiceTimeoutLocked(this);
+ }
+ }
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.mActivityTaskManager.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+ }
+
+ synchronized (mService) {
+ mService.mBatteryStatsService.noteProcessAnr(processName, uid);
+
+ if (isSilentANR) {
+ kill("bg anr", true);
+ return;
+ }
+
+ // Set the app's notResponding state, and look up the errorReportReceiver
+ makeAppNotRespondingLocked(activityShortComponentName,
+ annotation != null ? "ANR " + annotation : "ANR", info.toString());
+
+ // Bring up the infamous App Not Responding dialog
+ Message msg = Message.obtain();
+ msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
+ msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
+
+ mService.mUiHandler.sendMessage(msg);
+ }
+ }
+
+ private void makeAppNotRespondingLocked(String activity, String shortMsg, String longMsg) {
+ setNotResponding(true);
+ notRespondingReport = mService.mAppErrors.generateProcessError(this,
+ ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+ activity, shortMsg, longMsg, null);
+ startAppProblemLocked();
+ getWindowProcessController().stopFreezingActivities();
+ }
+
+ void startAppProblemLocked() {
+ // If this app is not running under the current user, then we can't give it a report button
+ // because that would require launching the report UI under a different user.
+ errorReportReceiver = null;
+
+ for (int userId : mService.mUserController.getCurrentProfileIds()) {
+ if (this.userId == userId) {
+ errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+ mService.mContext, info.packageName, info.flags);
+ }
+ }
+ mService.skipCurrentReceiverLocked(this);
+ }
+
+ private boolean isInterestingForBackgroundTraces() {
+ // The system_server is always considered interesting.
+ if (pid == MY_PID) {
+ return true;
+ }
+
+ // A package is considered interesting if any of the following is true :
+ //
+ // - It's displaying an activity.
+ // - It's the SystemUI.
+ // - It has an overlay or a top UI visible.
+ //
+ // NOTE: The check whether a given ProcessRecord belongs to the systemui
+ // process is a bit of a kludge, but the same pattern seems repeated at
+ // several places in the system server.
+ return isInterestingToUserLocked() ||
+ (info != null && "com.android.systemui".equals(info.packageName))
+ || (hasTopUi() || hasOverlayUi());
+ }
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index dd13e98..57f939f 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -33,15 +33,15 @@
import static android.os.Process.SYSTEM_UID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -103,7 +103,7 @@
* // 'X' tasks are trimmed.
*/
class RecentTasks {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_ATM;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index fa0cb47..1152165 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -21,9 +21,9 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -46,7 +46,7 @@
*/
public class SafeActivityOptions {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
private final int mOriginalCallingPid;
private final int mOriginalCallingUid;
diff --git a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
index fd34d18..111adec 100644
--- a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
@@ -35,8 +35,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -59,7 +59,7 @@
* The class that defines the default launch params for tasks.
*/
class TaskLaunchParamsModifier implements LaunchParamsModifier {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
private static final boolean DEBUG = false;
// A mask for SUPPORTS_SCREEN that indicates the activity supports resize.
@@ -114,6 +114,26 @@
private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout,
ActivityRecord activity, ActivityRecord source, ActivityOptions options,
LaunchParams currentParams, LaunchParams outParams) {
+ final ActivityRecord root;
+ if (task != null) {
+ root = task.getRootActivity() == null ? activity : task.getRootActivity();
+ } else {
+ root = activity;
+ }
+
+ // TODO: Investigate whether we can safely ignore all cases where we don't have root
+ // activity available. Note we can't know if the bounds are valid if we're not sure of the
+ // requested orientation of the root activity. Therefore if we found such a case we may need
+ // to pass the activity into this modifier in that case.
+ if (root == null) {
+ // There is a case that can lead us here. The caller is moving the top activity that is
+ // in a task that has multiple activities to PIP mode. For that the caller is creating a
+ // new task to host the activity so that we only move the top activity to PIP mode and
+ // keep other activities in the previous task. There is no point to apply the launch
+ // logic in this case.
+ return RESULT_SKIP;
+ }
+
// STEP 1: Determine the display to launch the activity/task.
final int displayId = getPreferredLaunchDisplay(options, source, currentParams);
outParams.mPreferredDisplayId = displayId;
@@ -123,24 +143,18 @@
+ display.getWindowingMode());
}
- final ActivityRecord root;
- if (task != null) {
- root = (task.getRootActivity() == null ? activity : task.getRootActivity());
- } else {
- root = activity;
- }
// STEP 2: Resolve launch windowing mode.
// STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
- // launch bounds from activity options, or size/gravity passed in layout. It also treat the
+ // launch bounds from activity options, or size/gravity passed in layout. It also treats the
// launch windowing mode in options as a suggestion for future resolution.
int launchMode = options != null ? options.getLaunchWindowingMode()
: WINDOWING_MODE_UNDEFINED;
// hasInitialBounds is set if either activity options or layout has specified bounds. If
// that's set we'll skip some adjustments later to avoid overriding the initial bounds.
boolean hasInitialBounds = false;
- final boolean canApplyFreeformPolicy =
- canApplyFreeformWindowPolicy(display, root, launchMode);
- if (mSupervisor.canUseActivityOptionsLaunchBounds(options) && canApplyFreeformPolicy) {
+ final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
+ if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
+ && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
hasInitialBounds = true;
launchMode = launchMode == WINDOWING_MODE_UNDEFINED
? WINDOWING_MODE_FREEFORM
@@ -279,10 +293,14 @@
return displayId;
}
- private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display,
- @NonNull ActivityRecord root, int launchMode) {
- return display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM
- || root.isResizeable();
+ private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
+ return mSupervisor.mService.mSupportsFreeformWindowManagement
+ && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ private boolean canApplyPipWindowPolicy(int launchMode) {
+ return mSupervisor.mService.mSupportsPictureInPicture
+ && launchMode == WINDOWING_MODE_PINNED;
}
private void getLayoutBounds(@NonNull ActivityDisplay display, @NonNull ActivityRecord root,
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 05b0d59..5f59163 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -45,16 +46,16 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
@@ -129,7 +130,7 @@
// TODO: Make package private again once move to WM package is complete.
public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
@@ -172,7 +173,6 @@
// code.
private static final int PERSIST_TASK_VERSION = 1;
- static final int INVALID_TASK_ID = -1;
private static final int INVALID_MIN_SIZE = -1;
/**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3a897c4..d2dd3db 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2242,21 +2242,15 @@
}
void stackSupervisorRemoveUser(int userId) {
- synchronized (mService) {
- mService.mStackSupervisor.removeUserLocked(userId);
- }
+ mService.mAtmInternal.removeUser(userId);
}
protected boolean stackSupervisorSwitchUser(int userId, UserState uss) {
- synchronized (mService) {
- return mService.mStackSupervisor.switchUserLocked(userId, uss);
- }
+ return mService.mAtmInternal.switchUser(userId, uss);
}
protected void stackSupervisorResumeFocusedStackTopActivity() {
- synchronized (mService) {
- mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
- }
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
protected void clearAllLockedTasks(String reason) {
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 366f95a..51d86d6 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -248,9 +248,9 @@
*
* @param tid the tid of the thread to set, or 0 to unset the current thread.
* @param pid the pid of the process owning the thread to set.
- * @param proc the ProcessRecord of the process owning the thread to set.
+ * @param proc the process owning the thread to set.
*/
- public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+ public void setPersistentVrThreadLocked(int tid, int pid, WindowProcessController proc) {
if (!hasPersistentVrFlagSet()) {
Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
return;
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index f6f4db6..1743dde 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -18,22 +18,26 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
import android.app.servertransaction.ConfigurationChangeItem;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -44,6 +48,7 @@
import android.util.Log;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.wm.ConfigurationContainer;
@@ -63,7 +68,7 @@
* calls are allowed to proceed.
*/
public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer> {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_ATM;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
@@ -87,6 +92,8 @@
private volatile IApplicationThread mThread;
// Currently desired scheduling class
private volatile int mCurSchedGroup;
+ // Currently computed process state
+ private volatile int mCurProcState = PROCESS_STATE_NONEXISTENT;
// Last reported process state;
private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
// are we in the process of crashing?
@@ -99,10 +106,34 @@
private volatile String mRequiredAbi;
// Running any services that are foreground?
private volatile boolean mHasForegroundServices;
+ // Running any activities that are foreground?
+ private volatile boolean mHasForegroundActivities;
+ // Are there any client services with activities?
+ private volatile boolean mHasClientActivities;
+ // Is this process currently showing a non-activity UI that the user is interacting with?
+ // E.g. The status bar when it is expanded, but not when it is minimized. When true the process
+ // will be set to use the ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost performance.
+ private volatile boolean mHasTopUi;
+ // Is the process currently showing a non-activity UI that overlays on-top of activity UIs on
+ // screen. E.g. display a window of type
+ // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY 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.
+ private volatile boolean mHasOverlayUi;
+ // Want to clean up resources from showing UI?
+ private volatile boolean mPendingUiClean;
+ // The time we sent the last interaction event
+ private volatile long mInteractionEventTime;
+ // When we became foreground for interaction purposes
+ private volatile long mFgInteractionTime;
+ // When (uptime) the process last became unimportant
+ private volatile long mWhenUnimportant;
// was app launched for debugging?
private volatile boolean mDebugging;
// Active instrumentation running in process?
private volatile boolean mInstrumenting;
+ // This process it perceptible by the user.
+ private volatile boolean mPerceptible;
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
@@ -161,6 +192,14 @@
return mCurSchedGroup;
}
+ public void setCurrentProcState(int curProcState) {
+ mCurProcState = curProcState;
+ }
+
+ int getCurrentProcState() {
+ return mCurProcState;
+ }
+
public void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
}
@@ -201,6 +240,78 @@
return mHasForegroundServices;
}
+ public void setHasForegroundActivities(boolean hasForegroundActivities) {
+ mHasForegroundActivities = hasForegroundActivities;
+ }
+
+ boolean hasForegroundActivities() {
+ return mHasForegroundActivities;
+ }
+
+ public void setHasClientActivities(boolean hasClientActivities) {
+ mHasClientActivities = hasClientActivities;
+ }
+
+ boolean hasClientActivities() {
+ return mHasClientActivities;
+ }
+
+ public void setHasTopUi(boolean hasTopUi) {
+ mHasTopUi = hasTopUi;
+ }
+
+ boolean hasTopUi() {
+ return mHasTopUi;
+ }
+
+ public void setHasOverlayUi(boolean hasOverlayUi) {
+ mHasOverlayUi = hasOverlayUi;
+ }
+
+ boolean hasOverlayUi() {
+ return mHasOverlayUi;
+ }
+
+ public void setPendingUiClean(boolean hasPendingUiClean) {
+ mPendingUiClean = hasPendingUiClean;
+ }
+
+ boolean hasPendingUiClean() {
+ return mPendingUiClean;
+ }
+
+ void postPendingUiCleanMsg(boolean pendingUiClean) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Message m = PooledLambda.obtainMessage(
+ WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
+ mAtm.mH.sendMessage(m);
+ }
+
+ public void setInteractionEventTime(long interactionEventTime) {
+ mInteractionEventTime = interactionEventTime;
+ }
+
+ long getInteractionEventTime() {
+ return mInteractionEventTime;
+ }
+
+ public void setFgInteractionTime(long fgInteractionTime) {
+ mFgInteractionTime = fgInteractionTime;
+ }
+
+ long getFgInteractionTime() {
+ return mFgInteractionTime;
+ }
+
+ public void setWhenUnimportant(long whenUnimportant) {
+ mWhenUnimportant = whenUnimportant;
+ }
+
+ long getWhenUnimportant() {
+ return mWhenUnimportant;
+ }
+
public void setRequiredAbi(String requiredAbi) {
mRequiredAbi = requiredAbi;
}
@@ -233,6 +344,14 @@
return mInstrumenting;
}
+ public void setPerceptible(boolean perceptible) {
+ mPerceptible = perceptible;
+ }
+
+ boolean isPerceptible() {
+ return mPerceptible;
+ }
+
@Override
protected int getChildCount() {
return 0;
@@ -500,12 +619,19 @@
final int activitiesSize = mActivities.size();
for (int i = activitiesSize - 1; i >= 0; i--) {
final ActivityRecord r = mActivities.get(i);
- if (r.mRelaunchReason != ActivityRecord.RELAUNCH_REASON_NONE) {
+ if (r.mRelaunchReason != RELAUNCH_REASON_NONE) {
return r.mRelaunchReason;
}
}
}
- return ActivityRecord.RELAUNCH_REASON_NONE;
+ return RELAUNCH_REASON_NONE;
+ }
+
+ public long getInputDispatchingTimeout() {
+ synchronized (mAtm.mGlobalLock) {
+ return isInstrumenting() || isUsingWrapper()
+ ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
+ }
}
void clearProfilerIfNeeded() {
@@ -532,14 +658,6 @@
WindowProcessListener::updateServiceConnectionActivities, mListener));
}
- void setPendingUiClean(boolean pendingUiClean) {
- if (mListener == null) return;
- // Posting on handler so WM lock isn't held when we call into AM.
- final Message m = PooledLambda.obtainMessage(
- WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
- mAtm.mH.sendMessage(m);
- }
-
void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
@@ -557,6 +675,35 @@
mAtm.mH.sendMessage(m);
}
+ void clearWaitingToKill() {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Message m = PooledLambda.obtainMessage(
+ WindowProcessListener::clearWaitingToKill, mListener);
+ mAtm.mH.sendMessage(m);
+ }
+
+ void addPackage(String pkg, long versionCode) {
+ // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+ // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+ if (mListener == null) return;
+ mListener.addPackage(pkg, versionCode);
+ }
+
+ ProfilerInfo onStartActivity(int topProcessState) {
+ // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+ // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+ if (mListener == null) return null;
+ return mListener.onStartActivity(topProcessState);
+ }
+
+ public void appDied() {
+ // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+ // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+ if (mListener == null) return;
+ mListener.appDied();
+ }
+
@Override
public void onConfigurationChanged(Configuration newGlobalConfig) {
super.onConfigurationChanged(newGlobalConfig);
@@ -654,4 +801,9 @@
pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ if (mListener != null) {
+ mListener.writeToProto(proto, fieldId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
index 2de3e37..4a7e6e8 100644
--- a/services/core/java/com/android/server/am/WindowProcessListener.java
+++ b/services/core/java/com/android/server/am/WindowProcessListener.java
@@ -16,6 +16,10 @@
package com.android.server.am;
+import android.app.ProfilerInfo;
+import android.content.pm.ApplicationInfo;
+import android.util.proto.ProtoOutputStream;
+
/**
* Interface used by the owner/creator of a process that owns windows to listen to changes from the
* WM side.
@@ -47,4 +51,17 @@
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
long getCpuTime();
+
+ /** Clears the waiting to kill reason for this process. */
+ void clearWaitingToKill();
+
+ /** Adds the package to the process. */
+ void addPackage(String pkg, long versionCode);
+
+ /** Called when we are in the process on starting an activity. */
+ ProfilerInfo onStartActivity(int topProcessState);
+
+ /** App died :(...oh well */
+ void appDied();
+ void writeToProto(ProtoOutputStream proto, long fieldId);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f0ff570..f56d8e6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4682,22 +4682,15 @@
}
}
- @Override
- public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
- {
- mDeviceLogger.log((new AudioEventLogger.StringEvent(
- "setHearingAidDeviceConnectionState state=" + state
- + " addr=" + device.getAddress())).printLog(TAG));
-
- setBluetoothHearingAidDeviceConnectionState(
- device, state, false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
- }
-
public int setBluetoothHearingAidDeviceConnectionState(
BluetoothDevice device, int state, boolean suppressNoisyIntent,
int musicDevice)
{
int delay;
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setHearingAidDeviceConnectionState state=" + state
+ + " addr=" + device.getAddress()
+ + " supprNoisy=" + suppressNoisyIntent)).printLog(TAG));
synchronized (mConnectedDevices) {
if (!suppressNoisyIntent) {
int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
@@ -5887,6 +5880,7 @@
address));
sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, null, 0);
+ setCurrentAudioRouteNameIfPossible(name);
}
private void onSendBecomingNoisyIntent() {
@@ -5908,7 +5902,7 @@
mConnectedDevices.remove(
makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
// Remove A2DP routes as well
- setCurrentAudioRouteName(null);
+ setCurrentAudioRouteNameIfPossible(null);
if (mDockAddress == address) {
mDockAddress = null;
}
@@ -5975,6 +5969,10 @@
address));
sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_HEARING_AID, 0, null, 0);
+ sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
+ AudioSystem.DEVICE_OUT_HEARING_AID, 0,
+ mStreamStates[AudioSystem.STREAM_MUSIC], 0);
+ setCurrentAudioRouteNameIfPossible(name);
}
// must be called synchronized on mConnectedDevices
@@ -5984,7 +5982,7 @@
mConnectedDevices.remove(
makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
// Remove Hearing Aid routes as well
- setCurrentAudioRouteName(null);
+ setCurrentAudioRouteNameIfPossible(null);
}
// must be called synchronized on mConnectedDevices
@@ -6029,7 +6027,6 @@
} else {
makeA2dpDeviceUnavailableNow(address);
}
- setCurrentAudioRouteName(null);
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
if (btDevice.isBluetoothDock()) {
// this could be a reconnection after a transient disconnection
@@ -6053,7 +6050,6 @@
}
makeA2dpDeviceAvailable(address, btDevice.getName(),
"onSetA2dpSinkConnectionState");
- setCurrentAudioRouteName(btDevice.getAliasName());
}
}
}
@@ -6105,25 +6101,35 @@
if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
makeHearingAidDeviceUnavailable(address);
- setCurrentAudioRouteName(null);
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
makeHearingAidDeviceAvailable(address, btDevice.getName(),
"onSetHearingAidConnectionState");
- setCurrentAudioRouteName(btDevice.getAliasName());
}
}
}
- private void setCurrentAudioRouteName(String name){
+ private void setCurrentAudioRouteNameIfPossible(String name) {
synchronized (mCurAudioRoutes) {
if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
- mCurAudioRoutes.bluetoothName = name;
- sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
- SENDMSG_NOOP, 0, 0, null, 0);
+ if (name != null || !isCurrentDeviceConnected()) {
+ mCurAudioRoutes.bluetoothName = name;
+ sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+ SENDMSG_NOOP, 0, 0, null, 0);
+ }
}
}
}
+ private boolean isCurrentDeviceConnected() {
+ for (int i = 0; i < mConnectedDevices.size(); i++) {
+ DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
+ if (TextUtils.equals(deviceSpec.mDeviceName, mCurAudioRoutes.bluetoothName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
{
if (DEBUG_DEVICES) {
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index 47e85b5..15468ff 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -126,9 +126,9 @@
public ProxyInfo getDefaultProxy() {
// This information is already available as a world read/writable jvm property.
synchronized (mProxyLock) {
- final ProxyInfo ret = mGlobalProxy;
- if ((ret == null) && mDefaultProxyEnabled) return mDefaultProxy;
- return ret;
+ if (mGlobalProxy != null) return mGlobalProxy;
+ if (mDefaultProxyEnabled) return mDefaultProxy;
+ return null;
}
}
@@ -204,11 +204,10 @@
*
* Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
* to do in a "sendProxyBroadcast" method.
- * @param proxyInfo the proxy spec, or null for no proxy.
*/
- // TODO : make the argument NonNull final and the method private
- public void sendProxyBroadcast(@Nullable ProxyInfo proxyInfo) {
- if (proxyInfo == null) proxyInfo = new ProxyInfo("", 0, "");
+ public void sendProxyBroadcast() {
+ final ProxyInfo defaultProxy = getDefaultProxy();
+ final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) return;
if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
@@ -269,7 +268,7 @@
Binder.restoreCallingIdentity(token);
}
- sendProxyBroadcast(mGlobalProxy == null ? mDefaultProxy : proxyInfo);
+ sendProxyBroadcast();
}
}
@@ -296,14 +295,14 @@
&& (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
&& proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
mGlobalProxy = proxyInfo;
- sendProxyBroadcast(mGlobalProxy);
+ sendProxyBroadcast();
return;
}
mDefaultProxy = proxyInfo;
if (mGlobalProxy != null) return;
if (mDefaultProxyEnabled) {
- sendProxyBroadcast(proxyInfo);
+ sendProxyBroadcast();
}
}
}
@@ -320,7 +319,7 @@
if (mDefaultProxyEnabled != enabled) {
mDefaultProxyEnabled = enabled;
if (mGlobalProxy == null && mDefaultProxy != null) {
- sendProxyBroadcast(enabled ? mDefaultProxy : null);
+ sendProxyBroadcast();
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c16d3cd..b148a2f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -307,19 +307,19 @@
private final class CecMessageBuffer {
private List<HdmiCecMessage> mBuffer = new ArrayList<>();
- public void bufferMessage(HdmiCecMessage message) {
+ public boolean bufferMessage(HdmiCecMessage message) {
switch (message.getOpcode()) {
case Constants.MESSAGE_ACTIVE_SOURCE:
bufferActiveSource(message);
- break;
+ return true;
case Constants.MESSAGE_IMAGE_VIEW_ON:
case Constants.MESSAGE_TEXT_VIEW_ON:
bufferImageOrTextViewOn(message);
- break;
+ return true;
// Add here if new message that needs to buffer
default:
// Do not need to buffer messages other than above
- break;
+ return false;
}
}
@@ -906,10 +906,6 @@
@ServiceThreadOnly
boolean handleCecCommand(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (!mAddressAllocated) {
- mCecMessageBuffer.bufferMessage(message);
- return true;
- }
int errorCode = mMessageValidator.isValid(message);
if (errorCode != HdmiCecMessageValidator.OK) {
// We'll not response on the messages with the invalid source or destination
@@ -919,7 +915,12 @@
}
return true;
}
- return dispatchMessageToLocalDevice(message);
+
+ if (dispatchMessageToLocalDevice(message)) {
+ return true;
+ }
+
+ return (!mAddressAllocated) ? mCecMessageBuffer.bufferMessage(message) : false;
}
void enableAudioReturnChannel(int portId, boolean enabled) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index d347a91..f7e871d 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -50,8 +50,7 @@
@Override
public void onSendCompleted(int error) {
if (error != SendMessageResult.SUCCESS) {
- tv().setSystemAudioMode(false);
- finish();
+ handleSystemAudioModeStatusTimeout();
}
}
});
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 4913e8b..c20079e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -197,7 +197,7 @@
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
- InputWindowHandle inputWindowHandle, boolean monitor);
+ InputWindowHandle inputWindowHandle, int displayId);
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
private static native int nativeInjectInputEvent(long ptr, InputEvent event,
@@ -473,15 +473,21 @@
/**
* Creates an input channel that will receive all input from the input dispatcher.
* @param inputChannelName The input channel name.
+ * @param displayId Target display id.
* @return The input channel.
*/
- public InputChannel monitorInput(String inputChannelName) {
+ public InputChannel monitorInput(String inputChannelName, int displayId) {
if (inputChannelName == null) {
throw new IllegalArgumentException("inputChannelName must not be null.");
}
+ if (displayId < Display.DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("displayId must >= 0.");
+ }
+
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
+ // Register channel for monitor.
+ nativeRegisterInputChannel(mPtr, inputChannels[0], null, displayId);
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
@@ -498,7 +504,8 @@
throw new IllegalArgumentException("inputChannel must not be null.");
}
- nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
+ // Register channel for normal.
+ nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, Display.INVALID_DISPLAY);
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a9b0d5c..d57214e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -15,7 +15,6 @@
package com.android.server.inputmethod;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -110,10 +109,12 @@
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -130,6 +131,9 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.HandlerCaller;
@@ -145,7 +149,6 @@
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -421,6 +424,7 @@
final IInputContext inputContext;
final int uid;
final int pid;
+ final int selfReportedDisplayId;
final InputBinding binding;
final ClientDeathRecipient clientDeathRecipient;
@@ -430,16 +434,18 @@
@Override
public String toString() {
return "ClientState{" + Integer.toHexString(
- System.identityHashCode(this)) + " uid " + uid
- + " pid " + pid + "}";
+ System.identityHashCode(this)) + " uid=" + uid
+ + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}";
}
ClientState(IInputMethodClient _client, IInputContext _inputContext,
- int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) {
+ int _uid, int _pid, int _selfReportedDisplayId,
+ ClientDeathRecipient _clientDeathRecipient) {
client = _client;
inputContext = _inputContext;
uid = _uid;
pid = _pid;
+ selfReportedDisplayId = _selfReportedDisplayId;
binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
clientDeathRecipient = _clientDeathRecipient;
}
@@ -498,6 +504,7 @@
*
* @see #mCurFocusedWindow
*/
+ @SoftInputModeFlags
int mCurFocusedWindowSoftInputMode;
/**
@@ -515,6 +522,7 @@
*
* @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
*/
+ @MissingMethodFlags
int mCurInputContextMissingMethods;
/**
@@ -688,20 +696,21 @@
final IBinder mImeToken;
@NonNull
final String mImeId;
- // @InputMethodClient.StartInputReason
+ @StartInputReason
final int mStartInputReason;
final boolean mRestarting;
@Nullable
final IBinder mTargetWindow;
@NonNull
final EditorInfo mEditorInfo;
+ @SoftInputModeFlags
final int mTargetWindowSoftInputMode;
final int mClientBindSequenceNumber;
StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId,
- /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting,
+ @StartInputReason int startInputReason, boolean restarting,
@Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo,
- int targetWindowSoftInputMode, int clientBindSequenceNumber) {
+ @SoftInputModeFlags int targetWindowSoftInputMode, int clientBindSequenceNumber) {
mSequenceNumber = sSequenceNumber.getAndIncrement();
mTimestamp = SystemClock.uptimeMillis();
mWallTime = System.currentTimeMillis();
@@ -769,13 +778,14 @@
String mImeTokenString;
@NonNull
String mImeId;
- /* @InputMethodClient.StartInputReason */
+ @StartInputReason
int mStartInputReason;
boolean mRestarting;
@NonNull
String mTargetWindowString;
@NonNull
EditorInfo mEditorInfo;
+ @SoftInputModeFlags
int mTargetWindowSoftInputMode;
int mClientBindSequenceNumber;
@@ -832,7 +842,7 @@
pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
+ " (timestamp=" + entry.mTimestamp + ")"
+ " reason="
- + InputMethodClient.getStartInputReason(entry.mStartInputReason)
+ + InputMethodDebug.startInputReasonToString(entry.mStartInputReason)
+ " restarting=" + entry.mRestarting);
pw.print(prefix);
@@ -844,7 +854,7 @@
+ " clientBindSeq=" + entry.mClientBindSequenceNumber);
pw.print(prefix);
- pw.println(" softInputMode=" + InputMethodClient.softInputModeToString(
+ pw.println(" softInputMode=" + InputMethodDebug.softInputModeToString(
entry.mTargetWindowSoftInputMode));
pw.print(prefix);
@@ -1497,7 +1507,7 @@
// TODO: Is it really possible that switchUserLocked() happens before system ready?
if (mSystemReady) {
hideCurrentInputLocked(0, null);
- resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_USER);
+ resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
buildInputMethodListLocked(initialUserSwitch);
if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
// This is the first time of the user switch and
@@ -1505,11 +1515,6 @@
resetDefaultImeLocked(mContext);
}
updateFromSettingsLocked(true);
- try {
- startInputInnerLocked();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Unexpected exception", e);
- }
}
if (initialUserSwitch) {
@@ -1585,12 +1590,6 @@
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
mSettings.getEnabledInputMethodListLocked(), currentUserId,
mContext.getBasePackageName());
-
- try {
- startInputInnerLocked();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Unexpected exception", e);
- }
}
}
}
@@ -1738,22 +1737,28 @@
* {@link InputMethodManagerService}.
*
* <p>As a general principle, IPCs from the application process that take
- * {@link InputMethodClient} will be rejected without this step.</p>
+ * {@link IInputMethodClient} will be rejected without this step.</p>
*
* @param client {@link android.os.Binder} proxy that is associated with the singleton instance
* of {@link android.view.inputmethod.InputMethodManager} that runs on the client
* process
* @param inputContext communication channel for the dummy
* {@link android.view.inputmethod.InputConnection}
+ * @param selfReportedDisplayId self-reported display ID to which the client is associated.
+ * Whether the client is still allowed to access to this display
+ * or not needs to be evaluated every time the client interacts
+ * with the display
*/
@Override
- public void addClient(IInputMethodClient client, IInputContext inputContext) {
+ public void addClient(IInputMethodClient client, IInputContext inputContext,
+ int selfReportedDisplayId) {
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
// TODO: Optimize this linear search.
for (ClientState state : mClients.values()) {
- if (state.uid == callerUid && state.pid == callerPid) {
+ if (state.uid == callerUid && state.pid == callerPid
+ && state.selfReportedDisplayId == selfReportedDisplayId) {
throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+ " is already registered");
}
@@ -1764,8 +1769,18 @@
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
- mClients.put(client.asBinder(),
- new ClientState(client, inputContext, callerUid, callerPid, deathRecipient));
+ // We cannot fully avoid race conditions where the client UID already lost the access to
+ // the given self-reported display ID, even if the client is not maliciously reporting
+ // a fake display ID. Unconditionally returning SecurityException just because the
+ // client doesn't pass display ID verification can cause many test failures hence not an
+ // option right now. At the same time
+ // context.getSystemService(InputMethodManager.class)
+ // is expected to return a valid non-null instance at any time if we do not choose to
+ // have the client crash. Thus we do not verify the display ID at all here. Instead we
+ // later check the display ID every time the client needs to interact with the specified
+ // display.
+ mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid,
+ callerPid, selfReportedDisplayId, deathRecipient));
}
}
@@ -1801,8 +1816,7 @@
}
}
- void unbindCurrentClientLocked(
- /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+ void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
+ mCurClient.client.asBinder());
@@ -1848,8 +1862,7 @@
@GuardedBy("mMethodMap")
@NonNull
- InputBindResult attachNewInputLocked(
- /* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
+ InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
if (!mBoundToMethod) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
@@ -1879,14 +1892,21 @@
@GuardedBy("mMethodMap")
@NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- @NonNull EditorInfo attribute, int controlFlags,
- /* @InputMethodClient.StartInputReason */ final int startInputReason) {
+ @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute, int controlFlags,
+ @StartInputReason int startInputReason) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
return InputBindResult.NO_IME;
}
+ if (!mSystemReady) {
+ // If the system is not yet ready, we shouldn't be running third
+ // party code.
+ return new InputBindResult(
+ InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
+ null, null, mCurMethodId, mCurSeq);
+ }
+
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
attribute.packageName)) {
Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
@@ -1894,12 +1914,19 @@
return InputBindResult.INVALID_PACKAGE_NAME;
}
+ if (!mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid)) {
+ // Wait, the client no longer has access to the display.
+ return InputBindResult.INVALID_DISPLAY_ID;
+ }
+ // Now that the display ID is validated, we trust cs.selfReportedDisplayId for this session.
+ final int displayIdToShowIme = cs.selfReportedDisplayId;
+
if (mCurClient != cs) {
// Was the keyguard locked when switching over to the new client?
mCurClientInKeyguard = isKeyguardLocked();
// If the client is changing, we need to switch over to the new
// one.
- unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_CLIENT);
+ unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
if (DEBUG) Slog.v(TAG, "switching to client: client="
+ cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
@@ -1918,9 +1945,10 @@
mCurAttribute = attribute;
// Check if the input method is changing.
- final int displayId = mWindowManagerInternal.getDisplayIdForWindow(
- mCurFocusedWindow);
- if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) {
+ // We expect the caller has already verified that the client is allowed to access this
+ // display ID.
+ if (mCurId != null && mCurId.equals(mCurMethodId)
+ && displayIdToShowIme == mCurTokenDisplayId) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -1954,23 +1982,6 @@
}
}
- return startInputInnerLocked();
- }
-
- @GuardedBy("mMethodMap")
- InputBindResult startInputInnerLocked() {
- if (mCurMethodId == null) {
- return InputBindResult.NO_IME;
- }
-
- if (!mSystemReady) {
- // If the system is not yet ready, we shouldn't be running third
- // party code.
- return new InputBindResult(
- InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, mCurMethodId, mCurSeq);
- }
-
InputMethodInfo info = mMethodMap.get(mCurMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
@@ -1984,14 +1995,13 @@
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
- final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow);
- mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY;
if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true;
mCurId = info.getId();
mCurToken = new Binder();
+ mCurTokenDisplayId = displayIdToShowIme;
try {
if (DEBUG) {
Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
@@ -2010,10 +2020,6 @@
}
@Override
- public void finishInput(IInputMethodClient client) {
- }
-
- @Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mMethodMap) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
@@ -2045,7 +2051,7 @@
mCurClient.curSession = new SessionState(mCurClient,
method, session, channel);
InputBindResult res = attachNewInputLocked(
- InputMethodClient.START_INPUT_REASON_SESSION_CREATED_BY_IME, true);
+ StartInputReason.SESSION_CREATED_BY_IME, true);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
MSG_BIND_CLIENT, mCurClient.client, res));
@@ -2087,8 +2093,7 @@
clearCurMethodLocked();
}
- void resetCurrentMethodAndClient(
- /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+ void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) {
mCurMethodId = null;
unbindCurrentMethodLocked();
unbindCurrentClientLocked(unbindClientReason);
@@ -2165,7 +2170,7 @@
mLastBindTime = SystemClock.uptimeMillis();
mShowRequested = mInputShown;
mInputShown = false;
- unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_DISCONNECT_IME);
+ unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
}
}
@@ -2476,12 +2481,12 @@
setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
- resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
+ resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
}
mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
- resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
+ resetCurrentMethodAndClient(UnbindReason.NO_IME);
}
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
@@ -2559,7 +2564,7 @@
intent.putExtra("input_method_id", id);
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
- unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
+ unbindCurrentClientLocked(UnbindReason.SWITCH_IME);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2584,7 +2589,8 @@
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
+ cs.selfReportedDisplayId)) {
Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
return false;
}
@@ -2668,7 +2674,8 @@
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
+ cs.selfReportedDisplayId)) {
if (DEBUG) {
Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
}
@@ -2731,11 +2738,10 @@
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
- int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- int unverifiedTargetSdkVersion) {
+ @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+ int controlFlags, @SoftInputModeFlags int softInputMode, int windowFlags,
+ @Nullable EditorInfo attribute, IInputContext inputContext,
+ @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -2746,7 +2752,7 @@
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
- + InputMethodClient.getStartInputReason(startInputReason)
+ + InputMethodDebug.startInputReasonToString(startInputReason)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " editorInfo=" + attribute);
return InputBindResult.NULL;
@@ -2756,27 +2762,27 @@
@NonNull
private InputBindResult startInputOrWindowGainedFocusInternal(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, @NonNull IBinder windowToken, int controlFlags,
- /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+ @StartInputReason int startInputReason, IInputMethodClient client,
+ @NonNull IBinder windowToken, int controlFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- int unverifiedTargetSdkVersion) {
+ @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
// Needs to check the validity before clearing calling identity
final boolean calledFromValidUser = calledFromValidUser();
InputBindResult res = null;
long ident = Binder.clearCallingIdentity();
try {
+ final int windowDisplayId =
+ mWindowManagerInternal.getDisplayIdForWindow(windowToken);
synchronized (mMethodMap) {
if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
- + InputMethodClient.getStartInputReason(startInputReason)
+ + InputMethodDebug.startInputReasonToString(startInputReason)
+ " client=" + client.asBinder()
+ " inputContext=" + inputContext
+ " missingMethods="
+ InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
+ " attribute=" + attribute
+ " controlFlags=#" + Integer.toHexString(controlFlags)
- + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+ + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
@@ -2785,8 +2791,15 @@
throw new IllegalArgumentException("unknown client "
+ client.asBinder());
}
+ if (cs.selfReportedDisplayId != windowDisplayId) {
+ Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch."
+ + " from client:" + cs.selfReportedDisplayId
+ + " from window:" + windowDisplayId);
+ return InputBindResult.DISPLAY_ID_MISMATCH;
+ }
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
+ if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
+ cs.selfReportedDisplayId)) {
// Check with the window manager to make sure this client actually
// has a window with focus. If not, reject. This is thread safe
// because if the focus changes some time before or after, the
@@ -2858,9 +2871,9 @@
// If focused display changed, we should unbind current method
// to make app window in previous display relayout after Ime
// window token removed.
- final int newFocusDisplayId =
- mWindowManagerInternal.getDisplayIdForWindow(windowToken);
- if (newFocusDisplayId != mCurTokenDisplayId) {
+ // Note that we can trust client's display ID as long as it matches
+ // to the display ID obtained from the window.
+ if (cs.selfReportedDisplayId != mCurTokenDisplayId) {
unbindCurrentMethodLocked();
}
}
@@ -2958,6 +2971,7 @@
}
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
+ // TODO(yukawa): multi-display support.
final int uid = Binder.getCallingUid();
if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
return true;
@@ -3034,6 +3048,7 @@
@Override
public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) {
+ // TODO(yukawa): Should we verify the display ID?
if (!calledFromValidUser()) {
return;
}
@@ -3229,6 +3244,7 @@
*/
@Override
public int getInputMethodWindowVisibleHeight() {
+ // TODO(yukawa): Should we verify the display ID?
return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
}
@@ -4564,7 +4580,7 @@
p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
p.println(" mCurFocusedWindow=" + mCurFocusedWindow
+ " softInputMode=" +
- InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode)
+ InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
p.println(" mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 0c66c5b..6989c33 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -55,6 +55,8 @@
* Each app can have a different default networks or different connectivity
* status due to user-requested network policies, so we need to check
* constraints on a per-UID basis.
+ *
+ * Test: atest com.android.server.job.controllers.ConnectivityControllerTest
*/
public final class ConnectivityController extends StateController implements
ConnectivityManager.OnNetworkActiveListener {
@@ -65,8 +67,9 @@
private final ConnectivityManager mConnManager;
private final NetworkPolicyManager mNetPolicyManager;
+ /** List of tracked jobs keyed by source UID. */
@GuardedBy("mLock")
- private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
+ private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
public ConnectivityController(JobSchedulerService service) {
super(service);
@@ -87,7 +90,12 @@
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.hasConnectivityConstraint()) {
updateConstraintsSatisfied(jobStatus);
- mTrackedJobs.add(jobStatus);
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
+ if (jobs == null) {
+ jobs = new ArraySet<>();
+ mTrackedJobs.put(jobStatus.getSourceUid(), jobs);
+ }
+ jobs.add(jobStatus);
jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
}
}
@@ -97,7 +105,10 @@
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) {
- mTrackedJobs.remove(jobStatus);
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
+ if (jobs != null) {
+ jobs.remove(jobStatus);
+ }
}
}
@@ -235,47 +246,26 @@
/**
* Update any jobs tracked by this controller that match given filters.
*
- * @param filterUid only update jobs belonging to this UID, or {@code -1} to
- * update all tracked jobs.
+ * @param filterUid only update jobs belonging to this UID, or {@code -1} to
+ * update all tracked jobs.
* @param filterNetwork only update jobs that would use this
- * {@link Network}, or {@code null} to update all tracked jobs.
+ * {@link Network}, or {@code null} to update all tracked jobs.
*/
private void updateTrackedJobs(int filterUid, Network filterNetwork) {
synchronized (mLock) {
// Since this is a really hot codepath, temporarily cache any
// answers that we get from ConnectivityManager.
- final SparseArray<Network> uidToNetwork = new SparseArray<>();
final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>();
boolean changed = false;
- for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
- final JobStatus js = mTrackedJobs.valueAt(i);
- final int uid = js.getSourceUid();
-
- final boolean uidMatch = (filterUid == -1 || filterUid == uid);
- if (uidMatch) {
- Network network = uidToNetwork.get(uid);
- if (network == null) {
- network = mConnManager.getActiveNetworkForUid(uid);
- uidToNetwork.put(uid, network);
- }
-
- // Update either when we have a network match, or when the
- // job hasn't yet been evaluated against the currently
- // active network; typically when we just lost a network.
- final boolean networkMatch = (filterNetwork == null
- || Objects.equals(filterNetwork, network));
- final boolean forceUpdate = !Objects.equals(js.network, network);
- if (networkMatch || forceUpdate) {
- final int netId = network != null ? network.netId : -1;
- NetworkCapabilities capabilities = networkToCapabilities.get(netId);
- if (capabilities == null) {
- capabilities = mConnManager.getNetworkCapabilities(network);
- networkToCapabilities.put(netId, capabilities);
- }
- changed |= updateConstraintsSatisfied(js, network, capabilities);
- }
+ if (filterUid == -1) {
+ for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
+ changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i),
+ filterNetwork, networkToCapabilities);
}
+ } else {
+ changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid),
+ filterNetwork, networkToCapabilities);
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
@@ -283,6 +273,36 @@
}
}
+ private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork,
+ SparseArray<NetworkCapabilities> networkToCapabilities) {
+ if (jobs == null || jobs.size() == 0) {
+ return false;
+ }
+
+ final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid());
+ final int netId = network != null ? network.netId : -1;
+ NetworkCapabilities capabilities = networkToCapabilities.get(netId);
+ if (capabilities == null) {
+ capabilities = mConnManager.getNetworkCapabilities(network);
+ networkToCapabilities.put(netId, capabilities);
+ }
+ final boolean networkMatch = (filterNetwork == null
+ || Objects.equals(filterNetwork, network));
+
+ boolean changed = false;
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ final JobStatus js = jobs.valueAt(i);
+
+ // Update either when we have a network match, or when the
+ // job hasn't yet been evaluated against the currently
+ // active network; typically when we just lost a network.
+ if (networkMatch || !Objects.equals(js.network, network)) {
+ changed |= updateConstraintsSatisfied(js, network, capabilities);
+ }
+ }
+ return changed;
+ }
+
/**
* We know the network has just come up. We want to run any jobs that are ready.
*/
@@ -290,12 +310,15 @@
public void onNetworkActive() {
synchronized (mLock) {
for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
- final JobStatus js = mTrackedJobs.valueAt(i);
- if (js.isReady()) {
- if (DEBUG) {
- Slog.d(TAG, "Running " + js + " due to network activity.");
+ final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
+ for (int j = jobs.size() - 1; j >= 0; j--) {
+ final JobStatus js = jobs.valueAt(j);
+ if (js.isReady()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running " + js + " due to network activity.");
+ }
+ mStateChangedListener.onRunJobNow(js);
}
- mStateChangedListener.onRunJobNow(js);
}
}
}
@@ -334,8 +357,12 @@
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
for (int i = 0; i < mTrackedJobs.size(); i++) {
- final JobStatus js = mTrackedJobs.valueAt(i);
- if (predicate.test(js)) {
+ final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
+ for (int j = 0; j < jobs.size(); j++) {
+ final JobStatus js = jobs.valueAt(j);
+ if (!predicate.test(js)) {
+ continue;
+ }
pw.print("#");
js.printUniqueId(pw);
pw.print(" from ");
@@ -355,20 +382,26 @@
final long mToken = proto.start(StateControllerProto.CONNECTIVITY);
for (int i = 0; i < mTrackedJobs.size(); i++) {
- final JobStatus js = mTrackedJobs.valueAt(i);
- if (!predicate.test(js)) {
- continue;
+ final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
+ for (int j = 0; j < jobs.size(); j++) {
+ final JobStatus js = jobs.valueAt(j);
+ if (!predicate.test(js)) {
+ continue;
+ }
+ final long jsToken = proto.start(
+ StateControllerProto.ConnectivityController.TRACKED_JOBS);
+ js.writeToShortProto(proto,
+ StateControllerProto.ConnectivityController.TrackedJob.INFO);
+ proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
+ js.getSourceUid());
+ NetworkRequest rn = js.getJob().getRequiredNetwork();
+ if (rn != null) {
+ rn.writeToProto(proto,
+ StateControllerProto.ConnectivityController.TrackedJob
+ .REQUIRED_NETWORK);
+ }
+ proto.end(jsToken);
}
- final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS);
- js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO);
- proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
- js.getSourceUid());
- NetworkRequest rn = js.getJob().getRequiredNetwork();
- if (rn != null) {
- rn.writeToProto(proto,
- StateControllerProto.ConnectivityController.TrackedJob.REQUIRED_NETWORK);
- }
- proto.end(jsToken);
}
proto.end(mToken);
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 3f8941d..4ece538d 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -57,6 +57,8 @@
* This isn't strictly necessary because each controller is only interested in a specific field,
* and the receivers that are listening for global state change will all run on the main looper,
* but we don't enforce that so this is safer.
+ *
+ * Test: atest com.android.server.job.controllers.JobStatusTest
* @hide
*/
public final class JobStatus {
@@ -154,7 +156,9 @@
// Constraints.
final int requiredConstraints;
+ private final int mRequiredConstraintsOfInterest;
int satisfiedConstraints = 0;
+ private int mSatisfiedConstraintsOfInterest = 0;
// Set to true if doze constraint was satisfied due to app being whitelisted.
public boolean dozeWhitelisted;
@@ -265,6 +269,28 @@
private long totalNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
+ /////// states change.
+
+ /**
+ * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
+ * should only run if its constraints are satisfied.
+ * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
+ */
+ private boolean mReadyDeadlineSatisfied;
+
+ /**
+ * The device isn't Dozing or this job will be in the foreground. This implicit constraint must
+ * be satisfied.
+ */
+ private boolean mReadyNotDozing;
+
+ /**
+ * The job is not restricted from running in the background (due to Battery Saver). This
+ * implicit constraint must be satisfied.
+ */
+ private boolean mReadyNotRestrictedInBg;
+
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
@@ -349,6 +375,8 @@
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
}
this.requiredConstraints = requiredConstraints;
+ mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
+ mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
@@ -865,7 +893,12 @@
}
boolean setDeadlineConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
+ if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
+ return true;
+ }
+ return false;
}
boolean setIdleConstraintSatisfied(boolean state) {
@@ -882,11 +915,21 @@
boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
dozeWhitelisted = whitelisted;
- return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
+ if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+ return true;
+ }
+ return false;
}
boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
+ if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyNotRestrictedInBg = state;
+ return true;
+ }
+ return false;
}
boolean setUidActive(final boolean newActiveState) {
@@ -903,6 +946,7 @@
return false;
}
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
+ mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
return true;
}
@@ -933,24 +977,15 @@
/**
* @return Whether or not this job is ready to run, based on its requirements. This is true if
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
- * TODO: This function is called a *lot*. We should probably just have it check an
- * already-computed boolean, which we updated whenever we see one of the states it depends
- * on here change.
*/
public boolean isReady() {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
// is an implementation detail. A periodic job should only run if its constraints are
// satisfied).
- // AppNotIdle implicit constraint must be satisfied
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
- final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
- && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
- final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
- || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
- final boolean notRestrictedInBg =
- (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
- return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
+ return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
+ || isConstraintsSatisfied());
}
static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -971,15 +1006,13 @@
return true;
}
- final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
-
- int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+ int sat = mSatisfiedConstraintsOfInterest;
if (overrideState == OVERRIDE_SOFT) {
// override: pretend all 'soft' requirements are satisfied
sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
}
- return (sat & req) == req;
+ return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
}
public boolean matches(int uid, int jobId) {
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index b29b7cf..99a04d7 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -20,6 +20,7 @@
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.IContexthub;
import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
@@ -57,9 +58,9 @@
private final ContextHubClientManager mClientManager;
/*
- * The ID of the hub that this client is attached to.
+ * The object describing the hub that this client is attached to.
*/
- private final int mAttachedContextHubId;
+ private final ContextHubInfo mAttachedContextHubInfo;
/*
* The host end point ID of this client.
@@ -85,11 +86,12 @@
/* package */ ContextHubClientBroker(
Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
- int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+ ContextHubInfo contextHubInfo, short hostEndPointId,
+ IContextHubClientCallback callback) {
mContext = context;
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
- mAttachedContextHubId = contextHubId;
+ mAttachedContextHubInfo = contextHubInfo;
mHostEndPointId = hostEndPointId;
mCallbackInterface = callback;
}
@@ -119,11 +121,12 @@
ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
mHostEndPointId, message);
+ int contextHubId = mAttachedContextHubInfo.getId();
try {
- result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+ result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
- + mAttachedContextHubId + ")", e);
+ + contextHubId + ")", e);
result = Result.UNKNOWN_FAILURE;
}
} else {
@@ -156,7 +159,7 @@
* @return the ID of the context hub this client is attached to
*/
/* package */ int getAttachedContextHubId() {
- return mAttachedContextHubId;
+ return mAttachedContextHubInfo.getId();
}
/**
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 4243f02..eda8c6f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -19,13 +19,13 @@
import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.ContextHubInfo;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
import android.hardware.location.NanoAppMessage;
import android.os.RemoteException;
import android.util.Log;
-import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -80,15 +80,15 @@
* Registers a new client with the service.
*
* @param clientCallback the callback interface of the client to register
- * @param contextHubId the ID of the hub this client is attached to
+ * @param contextHubInfo the object describing the hub this client is attached to
*
* @return the client interface
*
* @throws IllegalStateException if max number of clients have already registered
*/
/* package */ IContextHubClient registerClient(
- IContextHubClientCallback clientCallback, int contextHubId) {
- ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+ IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+ ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
try {
broker.attachDeathRecipient();
@@ -183,14 +183,14 @@
* manager.
*
* @param clientCallback the callback interface of the client to register
- * @param contextHubId the ID of the hub this client is attached to
+ * @param contextHubInfo the object describing the hub this client is attached to
*
* @return the ContextHubClientBroker object
*
* @throws IllegalStateException if max number of clients have already registered
*/
private synchronized ContextHubClientBroker createNewClientBroker(
- IContextHubClientCallback clientCallback, int contextHubId) {
+ IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
throw new IllegalStateException("Could not register client - max limit exceeded");
}
@@ -198,10 +198,11 @@
ContextHubClientBroker broker = null;
int id = mNextHostEndpointId;
for (int i = 0; i <= MAX_CLIENT_ID; i++) {
- if (!mHostEndPointIdToClientMap.containsKey((short)id)) {
+ if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
broker = new ContextHubClientBroker(
- mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
- mHostEndPointIdToClientMap.put((short)id, broker);
+ mContext, mContextHubProxy, this, contextHubInfo, (short) id,
+ clientCallback);
+ mHostEndPointIdToClientMap.put((short) id, broker);
mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
break;
}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 27509de..96e9337 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -170,8 +170,9 @@
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+ ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
IContextHubClient client = mClientManager.registerClient(
- createDefaultClientCallback(contextHubId), contextHubId);
+ createDefaultClientCallback(contextHubId), contextHubInfo);
defaultClientMap.put(contextHubId, client);
try {
@@ -623,7 +624,8 @@
throw new NullPointerException("Cannot register client with null callback");
}
- return mClientManager.registerClient(clientCallback, contextHubId);
+ ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
+ return mClientManager.registerClient(clientCallback, contextHubInfo);
}
/**
diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java
index af06d15..7304f07 100644
--- a/services/core/java/com/android/server/media/MediaUpdateService.java
+++ b/services/core/java/com/android/server/media/MediaUpdateService.java
@@ -22,7 +22,7 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.media.IMediaExtractorUpdateService;
+import android.media.IMediaUpdateService;
import android.os.Build;
import android.os.IBinder;
import android.os.Handler;
@@ -34,6 +34,7 @@
import android.util.Log;
import android.util.Slog;
import com.android.server.SystemService;
+import java.util.HashMap;
/** This class provides a system service that manages media framework updates. */
public class MediaUpdateService extends SystemService {
@@ -42,34 +43,40 @@
private static final String MEDIA_UPDATE_PACKAGE_NAME =
SystemProperties.get("ro.mediacomponents.package");
private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update";
-
- private IMediaExtractorUpdateService mMediaExtractorUpdateService;
- final Handler mHandler;
+ private static final String CODEC_UPDATE_SERVICE_NAME = "media.codec.update";
+ private static final String[] UPDATE_SERVICE_NAME_ARRAY = {
+ EXTRACTOR_UPDATE_SERVICE_NAME, CODEC_UPDATE_SERVICE_NAME,
+ };
+ private final HashMap<String, IMediaUpdateService> mUpdateServiceMap = new HashMap<>();
+ private final Handler mHandler = new Handler();
public MediaUpdateService(Context context) {
super(context);
- mHandler = new Handler();
}
@Override
public void onStart() {
if (("userdebug".equals(android.os.Build.TYPE) || "eng".equals(android.os.Build.TYPE))
&& !TextUtils.isEmpty(MEDIA_UPDATE_PACKAGE_NAME)) {
- connect();
+ for (String serviceName : UPDATE_SERVICE_NAME_ARRAY) {
+ connect(serviceName);
+ }
registerBroadcastReceiver();
}
}
- private void connect() {
- IBinder binder = ServiceManager.getService(EXTRACTOR_UPDATE_SERVICE_NAME);
+ private void connect(final String serviceName) {
+ IBinder binder = ServiceManager.getService(serviceName);
if (binder != null) {
try {
binder.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
- Slog.w(TAG, "mediaextractor died; reconnecting");
- mMediaExtractorUpdateService = null;
- connect();
+ Slog.w(TAG, "service " + serviceName + " died; reconnecting");
+ synchronized (mUpdateServiceMap) {
+ mUpdateServiceMap.remove(serviceName);
+ }
+ connect(serviceName);
}
}, 0);
} catch (Exception e) {
@@ -77,15 +84,18 @@
}
}
if (binder != null) {
- mMediaExtractorUpdateService = IMediaExtractorUpdateService.Stub.asInterface(binder);
+ synchronized (mUpdateServiceMap) {
+ mUpdateServiceMap.put(serviceName,
+ IMediaUpdateService.Stub.asInterface(binder));
+ }
mHandler.post(new Runnable() {
@Override
public void run() {
- packageStateChanged();
+ packageStateChanged(serviceName);
}
});
} else {
- Slog.w(TAG, EXTRACTOR_UPDATE_SERVICE_NAME + " not found.");
+ Slog.w(TAG, serviceName + " not found.");
}
}
@@ -106,13 +116,12 @@
// following ACTION_PACKAGE_ADDED case.
return;
}
- packageStateChanged();
- break;
+ // fall-thru
case Intent.ACTION_PACKAGE_CHANGED:
- packageStateChanged();
- break;
case Intent.ACTION_PACKAGE_ADDED:
- packageStateChanged();
+ for (String serviceName : UPDATE_SERVICE_NAME_ARRAY) {
+ packageStateChanged(serviceName);
+ }
break;
}
}
@@ -128,7 +137,7 @@
null /* broadcast permission */, null /* handler */);
}
- private void packageStateChanged() {
+ private void packageStateChanged(String serviceName) {
ApplicationInfo packageInfo = null;
boolean pluginsAvailable = false;
try {
@@ -144,17 +153,23 @@
+ " targetSdk:" + packageInfo.targetSdkVersion);
pluginsAvailable = false;
}
- loadExtractorPlugins(
+ loadPlugins(serviceName,
(packageInfo != null && pluginsAvailable) ? packageInfo.sourceDir : "");
}
- private void loadExtractorPlugins(String apkPath) {
+ private void loadPlugins(String serviceName, String apkPath) {
try {
- if (mMediaExtractorUpdateService != null) {
- mMediaExtractorUpdateService.loadPlugins(apkPath);
+ IMediaUpdateService service = null;
+ synchronized (serviceName) {
+ service = mUpdateServiceMap.get(serviceName);
+ }
+ if (service != null) {
+ service.loadPlugins(apkPath);
+ } else {
+ Slog.w(TAG, "service " + serviceName + " passed away");
}
} catch (Exception e) {
- Slog.w(TAG, "Error in loadPlugins", e);
+ Slog.w(TAG, "Error in loadPlugins for " + serviceName, e);
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 452b699..4f4b6bf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -72,6 +72,7 @@
static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
static final int NTWK_BLOCKED_BG_RESTRICT = 5;
static final int NTWK_ALLOWED_DEFAULT = 6;
+ static final int NTWK_ALLOWED_SYSTEM = 7;
private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 61d67b7..099671d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,6 +16,8 @@
package com.android.server.net;
+import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
+
import android.net.Network;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionPlan;
@@ -46,6 +48,28 @@
public abstract boolean isUidNetworkingBlocked(int uid, String ifname);
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ */
+ public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted) {
+ // Log of invoking internal function is disabled because it will be called very
+ // frequently. And metrics are unlikely needed on this method because the callers are
+ // external and this method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ /**
* Informs that an appId has been added or removed from the temp-powersave-whitelist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 48e09d7..d799642 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -99,6 +99,7 @@
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
@@ -4837,46 +4838,75 @@
final long startTime = mStatLogger.getTime();
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
-
- mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
-
- return ret;
- }
-
- private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) {
final int uidRules;
final boolean isBackgroundRestricted;
synchronized (mUidRulesFirstLock) {
uidRules = mUidRules.get(uid, RULE_NONE);
isBackgroundRestricted = mRestrictBackground;
}
- if (hasRule(uidRules, RULE_REJECT_ALL)) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
- return true;
+ final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, mLogger);
+
+ mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+ return ret;
+ }
+
+ private static boolean isSystem(int uid) {
+ return uid < Process.FIRST_APPLICATION_UID;
+ }
+
+ static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
+ final int reason;
+ // Networks are never blocked for system components
+ if (isSystem(uid)) {
+ reason = NTWK_ALLOWED_SYSTEM;
}
- if (!isNetworkMetered) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
- return false;
+ else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+ reason = NTWK_BLOCKED_POWER;
}
- if (hasRule(uidRules, RULE_REJECT_METERED)) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
- return true;
+ else if (!isNetworkMetered) {
+ reason = NTWK_ALLOWED_NON_METERED;
}
- if (hasRule(uidRules, RULE_ALLOW_METERED)) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
- return false;
+ else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+ reason = NTWK_BLOCKED_BLACKLIST;
}
- if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
- return false;
+ else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+ reason = NTWK_ALLOWED_WHITELIST;
}
- if (isBackgroundRestricted) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
- return true;
+ else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+ reason = NTWK_ALLOWED_TMP_WHITELIST;
}
- mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
- return false;
+ else if (isBackgroundRestricted) {
+ reason = NTWK_BLOCKED_BG_RESTRICT;
+ }
+ else {
+ reason = NTWK_ALLOWED_DEFAULT;
+ }
+
+ final boolean blocked;
+ switch(reason) {
+ case NTWK_ALLOWED_DEFAULT:
+ case NTWK_ALLOWED_NON_METERED:
+ case NTWK_ALLOWED_TMP_WHITELIST:
+ case NTWK_ALLOWED_WHITELIST:
+ case NTWK_ALLOWED_SYSTEM:
+ blocked = false;
+ break;
+ case NTWK_BLOCKED_POWER:
+ case NTWK_BLOCKED_BLACKLIST:
+ case NTWK_BLOCKED_BG_RESTRICT:
+ blocked = true;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ if (logger != null) {
+ logger.networkBlocked(uid, reason);
+ }
+
+ return blocked;
}
private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
@@ -4918,11 +4948,18 @@
public boolean isUidNetworkingBlocked(int uid, String ifname) {
final long startTime = mStatLogger.getTime();
+ final int uidRules;
+ final boolean isBackgroundRestricted;
+ synchronized (mUidRulesFirstLock) {
+ uidRules = mUidRules.get(uid, RULE_NONE);
+ isBackgroundRestricted = mRestrictBackground;
+ }
final boolean isNetworkMetered;
synchronized (mNetworkPoliciesSecondLock) {
isNetworkMetered = mMeteredIfaces.contains(ifname);
}
- final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+ final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, mLogger);
mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 506cc44..93b83ae 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -719,8 +719,7 @@
return;
}
final long now = System.currentTimeMillis();
- MetricsLogger.action(r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ MetricsLogger.action(r.getItemLogMaker()
.setType(MetricsEvent.TYPE_ACTION)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
@@ -865,8 +864,7 @@
r.stats.onExpansionChanged(userAction, expanded);
final long now = System.currentTimeMillis();
if (userAction) {
- MetricsLogger.action(r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ MetricsLogger.action(r.getItemLogMaker()
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
}
@@ -3624,7 +3622,7 @@
INotificationListener token, String pkg, UserHandle user,
NotificationChannelGroup group) throws RemoteException {
Preconditions.checkNotNull(user);
- verifyPrivilegedListener(token, user);
+ verifyPrivilegedListener(token, user, false);
createNotificationChannelGroup(
pkg, getUidForPackageAndUser(pkg, user), group, false, true);
savePolicyFile();
@@ -3637,7 +3635,7 @@
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(user);
- verifyPrivilegedListener(token, user);
+ verifyPrivilegedListener(token, user, false);
updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
}
@@ -3646,7 +3644,7 @@
INotificationListener token, String pkg, UserHandle user) throws RemoteException {
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(user);
- verifyPrivilegedListener(token, user);
+ verifyPrivilegedListener(token, user, true);
return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
false /* includeDeleted */);
@@ -3658,7 +3656,7 @@
INotificationListener token, String pkg, UserHandle user) throws RemoteException {
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(user);
- verifyPrivilegedListener(token, user);
+ verifyPrivilegedListener(token, user, true);
List<NotificationChannelGroup> groups = new ArrayList<>();
groups.addAll(mPreferencesHelper.getNotificationChannelGroups(
@@ -3666,13 +3664,18 @@
return new ParceledListSlice<>(groups);
}
- private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
+ private void verifyPrivilegedListener(INotificationListener token, UserHandle user,
+ boolean assistantAllowed) {
ManagedServiceInfo info;
synchronized (mNotificationLock) {
info = mListeners.checkServiceTokenLocked(token);
}
if (!hasCompanionDevice(info)) {
- throw new SecurityException(info + " does not have access");
+ synchronized (mNotificationLock) {
+ if (!assistantAllowed || !mAssistants.isServiceTokenValidLocked(info.service)) {
+ throw new SecurityException(info + " does not have access");
+ }
+ }
}
if (!info.enabledAndUserMatches(user.getIdentifier())) {
throw new SecurityException(info + " does not have access");
@@ -5842,8 +5845,7 @@
mArchive.record(r.sbn);
final long now = System.currentTimeMillis();
- final LogMaker logMaker = r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ final LogMaker logMaker = r.getItemLogMaker()
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(reason);
if (rank != -1 && count != -1) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 65ec580..e9f2718 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -611,6 +611,7 @@
}
public void applyAdjustments() {
+ long now = System.currentTimeMillis();
synchronized (mAdjustments) {
for (Adjustment adjustment: mAdjustments) {
Bundle signals = adjustment.getSignals();
@@ -618,17 +619,25 @@
final ArrayList<String> people =
adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
setPeopleOverride(people);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_PEOPLE, people.size()));
}
if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
final ArrayList<SnoozeCriterion> snoozeCriterionList =
adjustment.getSignals().getParcelableArrayList(
Adjustment.KEY_SNOOZE_CRITERIA);
setSnoozeCriteria(snoozeCriterionList);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SNOOZE_CRITERIA,
+ snoozeCriterionList.size()));
}
if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
final String groupOverrideKey =
adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
setOverrideGroupKey(groupOverrideKey);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_GROUP_KEY,
+ groupOverrideKey));
}
if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
// Only allow user sentiment update from assistant if user hasn't already
@@ -637,19 +646,31 @@
&& (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
setUserSentiment(adjustment.getSignals().getInt(
Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_USER_SENTIMENT,
+ getUserSentiment()));
}
}
if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
+ getSmartActions().size()));
}
if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
+ getSmartReplies().size()));
}
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
importance = Math.min(IMPORTANCE_HIGH, importance);
setAssistantImportance(importance);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_IMPORTANCE,
+ importance));
}
}
}
@@ -732,10 +753,10 @@
protected void calculateImportance() {
mImportance = calculateInitialImportance();
mImportanceExplanation = "app";
- if (getChannel().isImportanceLocked()) {
+ if (getChannel().hasUserSetImportance()) {
mImportanceExplanation = "user";
}
- if (!getChannel().isImportanceLocked() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+ if (!getChannel().hasUserSetImportance() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
mImportance = mAssistantImportance;
mImportanceExplanation = "asst";
}
@@ -1203,6 +1224,16 @@
return getLogMaker(System.currentTimeMillis());
}
+ public LogMaker getItemLogMaker() {
+ return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
+ }
+
+ public LogMaker getAdjustmentLogMaker() {
+ return getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT);
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 760f155..44b80c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1212,27 +1212,89 @@
}
private final class Metrics extends Callback {
- private static final String COUNTER_PREFIX = "dnd_mode_";
+ private static final String COUNTER_MODE_PREFIX = "dnd_mode_";
+ private static final String COUNTER_TYPE_PREFIX = "dnd_type_";
+ private static final int DND_OFF = 0;
+ private static final int DND_ON_MANUAL = 1;
+ private static final int DND_ON_AUTOMATIC = 2;
+ private static final String COUNTER_RULE = "dnd_rule_count";
private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
+ // Total silence, alarms only, priority only
private int mPreviousZenMode = -1;
- private long mBeginningMs = 0L;
+ private long mModeLogTimeMs = 0L;
+
+ private int mNumZenRules = -1;
+ private long mRuleCountLogTime = 0L;
+
+ // automatic (1) vs manual (0) vs dnd off (2)
+ private int mPreviousZenType = -1;
+ private long mTypeLogTimeMs = 0L;
@Override
void onZenModeChanged() {
emit();
}
+ @Override
+ void onConfigChanged() {
+ emit();
+ }
+
private void emit() {
mHandler.postMetricsTimer();
+ emitZenMode();
+ emitRules();
+ emitDndType();
+ }
+
+ private void emitZenMode() {
final long now = SystemClock.elapsedRealtime();
- final long since = (now - mBeginningMs);
+ final long since = (now - mModeLogTimeMs);
if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) {
if (mPreviousZenMode != -1) {
- MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since);
+ MetricsLogger.count(
+ mContext, COUNTER_MODE_PREFIX + mPreviousZenMode, (int) since);
}
mPreviousZenMode = mZenMode;
- mBeginningMs = now;
+ mModeLogTimeMs = now;
+ }
+ }
+
+ private void emitRules() {
+ final long now = SystemClock.elapsedRealtime();
+ final long since = (now - mRuleCountLogTime);
+ synchronized (mConfig) {
+ int numZenRules = mConfig.automaticRules.size();
+ if (mNumZenRules != numZenRules
+ || since > MINIMUM_LOG_PERIOD_MS) {
+ if (mNumZenRules != -1) {
+ MetricsLogger.count(mContext, COUNTER_RULE,
+ numZenRules - mNumZenRules);
+ }
+ mNumZenRules = numZenRules;
+
+ mRuleCountLogTime = since;
+ }
+ }
+ }
+
+ private void emitDndType() {
+ final long now = SystemClock.elapsedRealtime();
+ final long since = (now - mTypeLogTimeMs);
+ synchronized (mConfig) {
+ boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
+ int zenType = !dndOn ? DND_OFF
+ : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
+ if (zenType != mPreviousZenType
+ || since > MINIMUM_LOG_PERIOD_MS) {
+ if (mPreviousZenType != -1) {
+ MetricsLogger.count(
+ mContext, COUNTER_TYPE_PREFIX + mPreviousZenType, (int) since);
+ }
+ mTypeLogTimeMs = now;
+ mPreviousZenType = zenType;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index a08c189..404f152 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -659,15 +659,6 @@
try {
final PackageManagerInternal pmInt =
LocalServices.getService(PackageManagerInternal.class);
- ActivityInfo info = pmInt.getActivityInfo(component,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- callingUid, user.getIdentifier());
- if (!info.exported) {
- throw new SecurityException("Cannot launch non-exported components "
- + component);
- }
-
// Check that the component actually has Intent.CATEGORY_LAUCNCHER
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
@@ -680,6 +671,11 @@
ActivityInfo activityInfo = apps.get(i).activityInfo;
if (activityInfo.packageName.equals(component.getPackageName()) &&
activityInfo.name.equals(component.getClassName())) {
+ if (!activityInfo.exported) {
+ throw new SecurityException("Cannot launch non-exported components "
+ + component);
+ }
+
// Found an activity with category launcher that matches
// this component so ok to launch.
launchIntent.setPackage(null);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 33bb15f..9399ebf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -188,6 +188,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
@@ -9538,7 +9539,7 @@
}
}
}
- if (deleteSandboxData) {
+ if (deleteSandboxData && getStorageManagerInternal() != null) {
getStorageManagerInternal().destroySandboxForApp(pkg.packageName, realUserId);
}
} catch (PackageManagerException e) {
@@ -12157,7 +12158,9 @@
if (mPackageListObservers.size() == 0) {
return;
}
- observers = (PackageListObserver[]) mPackageListObservers.toArray();
+ final PackageListObserver[] observerArray =
+ new PackageListObserver[mPackageListObservers.size()];
+ observers = mPackageListObservers.toArray(observerArray);
}
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onPackageAdded(packageName);
@@ -12177,7 +12180,9 @@
if (mPackageListObservers.size() == 0) {
return;
}
- observers = (PackageListObserver[]) mPackageListObservers.toArray();
+ final PackageListObserver[] observerArray =
+ new PackageListObserver[mPackageListObservers.size()];
+ observers = mPackageListObservers.toArray(observerArray);
}
for (int i = observers.length - 1; i >= 0; --i) {
observers[i].onPackageRemoved(packageName);
@@ -12712,8 +12717,8 @@
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
- PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage,
- String callingPackage, int userId) {
+ PersistableBundle appExtras, PersistableBundle launcherExtras,
+ SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
"setPackagesSuspendedAsUser");
@@ -12758,7 +12763,7 @@
unactionedPackages.add(packageName);
continue;
}
- pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
+ pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
launcherExtras, userId);
changedPackagesList.add(packageName);
changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
@@ -17812,7 +17817,7 @@
false /*hidden*/,
false /*suspended*/,
null /*suspendingPackage*/,
- null /*dialogMessage*/,
+ null /*dialogInfo*/,
null /*suspendedAppExtras*/,
null /*suspendedLauncherExtras*/,
false /*instantApp*/,
@@ -22581,10 +22586,10 @@
}
@Override
- public String getSuspendedDialogMessage(String suspendedPackage, int userId) {
+ public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
- return (ps != null) ? ps.readUserState(userId).dialogMessage : null;
+ return (ps != null) ? ps.readUserState(userId).dialogInfo : null;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 93729d1..e25cca4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -51,6 +51,7 @@
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
@@ -1701,9 +1702,18 @@
}
final String callingPackage =
(Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
+
+ final SuspendDialogInfo info;
+ if (!TextUtils.isEmpty(dialogMessage)) {
+ info = new SuspendDialogInfo.Builder()
+ .setMessage(dialogMessage)
+ .build();
+ } else {
+ info = null;
+ }
try {
mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
- appExtras, launcherExtras, dialogMessage, callingPackage, userId);
+ appExtras, launcherExtras, info, callingPackage, userId);
pw.println("Package " + packageName + " new suspended state: "
+ mInterface.isPackageSuspendedForUser(packageName, userId));
return 0;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index fd6aceb..3c22f07 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
+import android.content.pm.SuspendDialogInfo;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArraySet;
@@ -395,12 +396,12 @@
return readUserState(userId).suspended;
}
- void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
+ void setSuspended(boolean suspended, String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
existingUserState.suspended = suspended;
existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
- existingUserState.dialogMessage = suspended ? dialogMessage : null;
+ existingUserState.dialogInfo = suspended ? dialogInfo : null;
existingUserState.suspendedAppExtras = suspended ? appExtras : null;
existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
}
@@ -423,7 +424,7 @@
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
- String dialogMessage, PersistableBundle suspendedAppExtras,
+ SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
PersistableBundle suspendedLauncherExtras, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
@@ -438,7 +439,7 @@
state.hidden = hidden;
state.suspended = suspended;
state.suspendingPackage = suspendingPackage;
- state.dialogMessage = dialogMessage;
+ state.dialogInfo = dialogInfo;
state.suspendedAppExtras = suspendedAppExtras;
state.suspendedLauncherExtras = suspendedLauncherExtras;
state.lastDisableAppCaller = lastDisableAppCaller;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5c88e06..e2818b7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -49,6 +49,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.net.Uri;
@@ -203,6 +204,7 @@
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_DEFAULT_DIALER = "default-dialer";
private static final String TAG_VERSION = "version";
+ private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info";
private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
@@ -222,6 +224,10 @@
private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_SUSPENDED = "suspended";
private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
+ /**
+ * @deprecated Legacy attribute, kept only for upgrading from P builds.
+ */
+ @Deprecated
private static final String ATTR_SUSPEND_DIALOG_MESSAGE = "suspend_dialog_message";
// Legacy, uninstall blocks are stored separately.
@Deprecated
@@ -730,7 +736,7 @@
false /*hidden*/,
false /*suspended*/,
null /*suspendingPackage*/,
- null /*dialogMessage*/,
+ null /*dialogInfo*/,
null /*suspendedAppExtras*/,
null /*suspendedLauncherExtras*/,
instantApp,
@@ -1620,7 +1626,7 @@
false /*hidden*/,
false /*suspended*/,
null /*suspendingPackage*/,
- null /*dialogMessage*/,
+ null /*dialogInfo*/,
null /*suspendedAppExtras*/,
null /*suspendedLauncherExtras*/,
false /*instantApp*/,
@@ -1730,6 +1736,7 @@
ArraySet<String> disabledComponents = null;
PersistableBundle suspendedAppExtras = null;
PersistableBundle suspendedLauncherExtras = null;
+ SuspendDialogInfo suspendDialogInfo = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1752,20 +1759,28 @@
case TAG_SUSPENDED_LAUNCHER_EXTRAS:
suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
break;
+ case TAG_SUSPENDED_DIALOG_INFO:
+ suspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
+ break;
default:
Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
+ TAG_PACKAGE);
}
}
+ if (suspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) {
+ suspendDialogInfo = new SuspendDialogInfo.Builder()
+ .setMessage(dialogMessage)
+ .build();
+ }
if (blockUninstall) {
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, suspended, suspendingPackage, dialogMessage, suspendedAppExtras,
- suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
- enabledComponents, disabledComponents, verifState, linkGeneration,
- installReason, harmfulAppWarning);
+ hidden, suspended, suspendingPackage, suspendDialogInfo,
+ suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
+ enabledCaller, enabledComponents, disabledComponents, verifState,
+ linkGeneration, installReason, harmfulAppWarning);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -2076,9 +2091,10 @@
serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
ustate.suspendingPackage);
}
- if (ustate.dialogMessage != null) {
- serializer.attribute(null, ATTR_SUSPEND_DIALOG_MESSAGE,
- ustate.dialogMessage);
+ if (ustate.dialogInfo != null) {
+ serializer.startTag(null, TAG_SUSPENDED_DIALOG_INFO);
+ ustate.dialogInfo.saveToXml(serializer);
+ serializer.endTag(null, TAG_SUSPENDED_DIALOG_INFO);
}
if (ustate.suspendedAppExtras != null) {
serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
@@ -2618,6 +2634,10 @@
writeKernelMappingLPr(ps);
}
+ for (final SharedUserSetting sus : mSharedUsers.values()) {
+ knownSet.remove(sus.getSandboxName());
+ }
+
// Remove any unclaimed mappings
for (int i = 0; i < knownSet.size(); i++) {
final String name = knownSet.valueAt(i);
@@ -2628,30 +2648,42 @@
}
}
+ void writeKernelMappingLPr(SharedUserSetting sus) {
+ if (mKernelMappingFilename == null || sus == null || sus.name == null) return;
+
+ writeKernelMappingLPr(sus.getSandboxName(), sus.userId, sus.getNotInstalledUserIds());
+ }
+
void writeKernelMappingLPr(PackageSetting ps) {
if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
- KernelPackageState cur = mKernelMapping.get(ps.name);
+ writeKernelMappingLPr(ps.name, ps.appId, ps.getNotInstalledUserIds());
+ if (ps.sharedUser != null) {
+ writeKernelMappingLPr(ps.sharedUser);
+ }
+ }
+
+ void writeKernelMappingLPr(String name, int appId, int[] excludedUserIds) {
+ KernelPackageState cur = mKernelMapping.get(name);
final boolean firstTime = cur == null;
- int[] excludedUserIds = ps.getNotInstalledUserIds();
final boolean userIdsChanged = firstTime
|| !Arrays.equals(excludedUserIds, cur.excludedUserIds);
// Package directory
- final File dir = new File(mKernelMappingFilename, ps.name);
+ final File dir = new File(mKernelMappingFilename, name);
if (firstTime) {
dir.mkdir();
// Create a new mapping state
cur = new KernelPackageState();
- mKernelMapping.put(ps.name, cur);
+ mKernelMapping.put(name, cur);
}
// If mapping is incorrect or non-existent, write the appid file
- if (cur.appId != ps.appId) {
+ if (cur.appId != appId) {
final File appIdFile = new File(dir, "appid");
- writeIntToFile(appIdFile, ps.appId);
- if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
+ writeIntToFile(appIdFile, appId);
+ if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + name + " to " + appId);
}
if (userIdsChanged) {
@@ -2661,7 +2693,7 @@
excludedUserIds[i])) {
writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]);
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to "
- + ps.name + "/excluded_userids");
+ + name + "/excluded_userids");
}
}
// Build the inclusion list -- the ids to remove from the exclusion list
@@ -2671,7 +2703,7 @@
writeIntToFile(new File(dir, "clear_userid"),
cur.excludedUserIds[i]);
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to "
- + ps.name + "/clear_userid");
+ + name + "/clear_userid");
}
}
@@ -4737,8 +4769,8 @@
final PackageUserState pus = ps.readUserState(user.id);
pw.print(" suspendingPackage=");
pw.print(pus.suspendingPackage);
- pw.print(" dialogMessage=");
- pw.print(pus.dialogMessage);
+ pw.print(" dialogInfo=");
+ pw.print(pus.dialogInfo);
}
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 1a8b2af..32826e5 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -23,6 +23,10 @@
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.util.EmptyArray;
+
import java.util.ArrayList;
import java.util.List;
@@ -144,6 +148,28 @@
}
}
+ /** Returns userIds which doesn't have any packages with this sharedUserId */
+ public int[] getNotInstalledUserIds() {
+ int[] excludedUserIds = null;
+ for (PackageSetting ps : packages) {
+ final int[] userIds = ps.getNotInstalledUserIds();
+ if (excludedUserIds == null) {
+ excludedUserIds = userIds;
+ } else {
+ for (int userId : excludedUserIds) {
+ if (!ArrayUtils.contains(userIds, userId)) {
+ excludedUserIds = ArrayUtils.removeInt(excludedUserIds, userId);
+ }
+ }
+ }
+ }
+ return excludedUserIds == null ? EmptyArray.INT : excludedUserIds;
+ }
+
+ public String getSandboxName() {
+ return "shared:" + name;
+ }
+
/** Updates all fields in this shared user setting from another. */
public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
copyFrom(sharedUser);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 1315502..dd04652 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -127,7 +127,8 @@
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
UserManager.DISALLOW_AMBIENT_DISPLAY,
UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
- UserManager.DISALLOW_PRINTING
+ UserManager.DISALLOW_PRINTING,
+ UserManager.DISALLOW_CONFIG_PRIVATE_DNS
});
/**
@@ -163,7 +164,8 @@
* User restrictions that cannot be set by profile owners. Applied to all users.
*/
private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
- UserManager.DISALLOW_USER_SWITCH
+ UserManager.DISALLOW_USER_SWITCH,
+ UserManager.DISALLOW_CONFIG_PRIVATE_DNS
);
/**
@@ -741,6 +743,10 @@
restriction = UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT;
break;
+ case android.provider.Settings.Global.PRIVATE_DNS_MODE:
+ case android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER:
+ restriction = UserManager.DISALLOW_CONFIG_PRIVATE_DNS;
+ break;
default:
if (setting.startsWith(Settings.Global.DATA_ROAMING)) {
if ("0".equals(value)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 0a93653..e6a018a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -31,9 +31,11 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackagesProvider;
import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
+import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.media.RingtoneManager;
@@ -1182,12 +1184,16 @@
final int permissionGrantCount = permissionGrants.size();
for (int j = 0; j < permissionGrantCount; j++) {
DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+ if (!isPermissionDangerous(permissionGrant.name)) {
+ Log.w(TAG, "Ignoring permission " + permissionGrant.name
+ + " which isn't dangerous");
+ continue;
+ }
if (permissions == null) {
permissions = new ArraySet<>();
} else {
permissions.clear();
}
- permissions.add(permissionGrant.name);
grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, userId);
}
}
@@ -1350,6 +1356,16 @@
&& pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
}
+ private boolean isPermissionDangerous(String name) {
+ try {
+ final PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(name, 0);
+ return (pi.getProtectionFlags() & PermissionInfo.PROTECTION_DANGEROUS) != 0;
+ } catch (NameNotFoundException e) {
+ // When unknown assume it's dangerous to be on the safe side
+ return true;
+ }
+ }
+
private static final class DefaultPermissionGrant {
final String name;
final boolean fixed;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 06ee935..c5cee32 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2061,7 +2061,8 @@
}
});
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
- mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
+ //TODO (b/111365687) : make system context per display.
+ mWindowManagerFuncs.registerPointerEventListener(mSystemGestures, DEFAULT_DISPLAY);
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
@@ -2258,13 +2259,16 @@
WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
wm.addView(mPointerLocationView, lp);
- mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView);
+ //TODO (b/111365687) : make system context per display.
+ mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView, DEFAULT_DISPLAY);
}
}
private void disablePointerLocation() {
if (mPointerLocationView != null) {
- mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView);
+ //TODO (b/111365687) : make system context per display.
+ mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView,
+ DEFAULT_DISPLAY);
WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
wm.removeView(mPointerLocationView);
mPointerLocationView = null;
@@ -6860,37 +6864,6 @@
}
@Override
- public int getUserRotationMode() {
- return Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
- WindowManagerPolicy.USER_ROTATION_FREE :
- WindowManagerPolicy.USER_ROTATION_LOCKED;
- }
-
- // User rotation: to be used when all else fails in assigning an orientation to the device
- @Override
- public void setUserRotationMode(int mode, int rot) {
- ContentResolver res = mContext.getContentResolver();
-
- // mUserRotationMode and mUserRotation will be assigned by the content observer
- if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
- Settings.System.putIntForUser(res,
- Settings.System.USER_ROTATION,
- rot,
- UserHandle.USER_CURRENT);
- Settings.System.putIntForUser(res,
- Settings.System.ACCELEROMETER_ROTATION,
- 0,
- UserHandle.USER_CURRENT);
- } else {
- Settings.System.putIntForUser(res,
- Settings.System.ACCELEROMETER_ROTATION,
- 1,
- UserHandle.USER_CURRENT);
- }
- }
-
- @Override
public void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
if (safeMode) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 27ab3ef..db13cbc 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -82,7 +82,6 @@
import android.view.IWindowManager;
import android.view.InputEventReceiver;
import android.view.KeyEvent;
-import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants;
@@ -559,10 +558,10 @@
public Object getWindowManagerLock();
/** Register a system listener for touch events */
- void registerPointerEventListener(PointerEventListener listener);
+ void registerPointerEventListener(PointerEventListener listener, int displayId);
/** Unregister a system listener for touch events */
- void unregisterPointerEventListener(PointerEventListener listener);
+ void unregisterPointerEventListener(PointerEventListener listener, int displayId);
/**
* @return The content insets of the docked divider window.
@@ -1483,26 +1482,6 @@
public void keepScreenOnStoppedLw();
/**
- * Gets the current user rotation mode.
- *
- * @return The rotation mode.
- *
- * @see #USER_ROTATION_LOCKED
- * @see #USER_ROTATION_FREE
- */
- @UserRotationMode
- public int getUserRotationMode();
-
- /**
- * Inform the policy that the user has chosen a preferred orientation ("rotation lock").
- *
- * @param mode One of {@link #USER_ROTATION_LOCKED} or {@link #USER_ROTATION_FREE}.
- * @param rotation One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
- * {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
- */
- public void setUserRotationMode(@UserRotationMode int mode, @Surface.Rotation int rotation);
-
- /**
* Called when a new system UI visibility is being reported, allowing
* the policy to adjust what is actually reported.
* @param visibility The raw visibility reported by the status bar.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index be24c71..5569822 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -243,7 +243,7 @@
}
/**
- * Called by {@link PowerManagerService} to update the battery saver stete.
+ * Called by {@link PowerManagerService} to update the battery saver state.
*/
public void enableBatterySaver(boolean enable, int reason) {
synchronized (mLock) {
@@ -290,8 +290,8 @@
* This method is called only in the following cases:
* - When battery saver becomes activated.
* - When battery saver becomes deactivated.
- * - When battery saver is on the interactive state changes.
- * - When battery saver is on the battery saver policy changes.
+ * - When battery saver is on and the interactive state changes.
+ * - When battery saver is on and the battery saver policy changes.
*/
void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
final LowPowerModeListener[] listeners;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1abaaf2..c162afb 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -43,12 +43,15 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
@@ -148,6 +151,13 @@
public static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
public static final int DEATH_THRESHOLD = 10;
+
+ static final class CompanionHandler extends Handler {
+ CompanionHandler(Looper looper) {
+ super(looper);
+ }
+ }
+
private final Context mContext;
private final AlarmManager mAlarmManager;
@GuardedBy("sStatsdLock")
@@ -164,15 +174,11 @@
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
private IWifiManager mWifiManager = null;
private TelephonyManager mTelephony = null;
- private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
- private final StatFs mStatFsSystem =
- new StatFs(Environment.getRootDirectory().getAbsolutePath());
- private final StatFs mStatFsTemp =
- new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
@GuardedBy("sStatsdLock")
private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
@GuardedBy("sStatsdLock")
private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
+ private final CompanionHandler mHandler;
private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -251,6 +257,11 @@
} else {
Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
}
+
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mHandler = new CompanionHandler(handlerThread.getLooper());
+
}
@Override
@@ -498,7 +509,7 @@
// only fire when it awakens.
// AlarmManager will automatically cancel any previous mAnomalyAlarmListener alarm.
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".anomaly",
- mAnomalyAlarmListener, null);
+ mAnomalyAlarmListener, mHandler);
} finally {
Binder.restoreCallingIdentity(callingToken);
}
@@ -529,7 +540,7 @@
// using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
// only fire when it awakens.
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
- mPeriodicAlarmListener, null);
+ mPeriodicAlarmListener, mHandler);
} finally {
Binder.restoreCallingIdentity(callingToken);
}
@@ -561,7 +572,7 @@
// using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
// only fire when it awakens.
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
- mPullingAlarmListener, null);
+ mPullingAlarmListener, mHandler);
} finally {
Binder.restoreCallingIdentity(callingToken);
}
@@ -756,7 +767,7 @@
private void pullBluetoothBytesTransfer(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = pullBluetoothData();
+ BluetoothActivityEnergyInfo info = fetchBluetoothData();
if (info.getUidTraffic() != null) {
for (UidTraffic traffic : info.getUidTraffic()) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
@@ -868,9 +879,12 @@
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
- if (mWifiManager == null) {
- mWifiManager =
- IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
+ synchronized (this) {
+ if (mWifiManager == null) {
+ mWifiManager =
+ IWifiManager.Stub.asInterface(
+ ServiceManager.getService(Context.WIFI_SERVICE));
+ }
}
if (mWifiManager != null) {
try {
@@ -900,8 +914,10 @@
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
long token = Binder.clearCallingIdentity();
- if (mTelephony == null) {
- mTelephony = TelephonyManager.from(mContext);
+ synchronized (this) {
+ if (mTelephony == null) {
+ mTelephony = TelephonyManager.from(mContext);
+ }
}
if (mTelephony != null) {
SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
@@ -925,7 +941,7 @@
private void pullBluetoothActivityInfo(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = pullBluetoothData();
+ BluetoothActivityEnergyInfo info = fetchBluetoothData();
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeLong(info.getTimeStamp());
e.writeInt(info.getBluetoothStackState());
@@ -936,7 +952,7 @@
pulledData.add(e);
}
- private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
+ private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
@@ -985,6 +1001,23 @@
}
}
+ private void pullNativeProcessMemoryState(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> processMemoryStates = LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForNativeProcesses();
+ for (ProcessMemoryState processMemoryState : processMemoryStates) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processMemoryState.uid);
+ e.writeString(processMemoryState.processName);
+ e.writeLong(processMemoryState.pgfault);
+ e.writeLong(processMemoryState.pgmajfault);
+ e.writeLong(processMemoryState.rssInBytes);
+ e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+ pulledData.add(e);
+ }
+ }
+
private void pullBinderCallsStats(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -1056,6 +1089,11 @@
e.writeLong(entry.totalLatencyMicros);
e.writeLong(entry.cpuUsageMicros);
e.writeBoolean(entry.isInteractive);
+ e.writeLong(entry.maxCpuUsageMicros);
+ e.writeLong(entry.maxLatencyMicros);
+ e.writeLong(entry.recordedDelayMessageCount);
+ e.writeLong(entry.delayMillis);
+ e.writeLong(entry.maxDelayMillis);
pulledData.add(e);
}
}
@@ -1285,30 +1323,35 @@
private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- try {
- long lastHighWaterMark = readProcStatsHighWaterMark(section);
- List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
- long highWaterMark = mProcessStats.getCommittedStats(
- lastHighWaterMark, section, true, statsFiles);
- if (statsFiles.size() != 1) {
- return;
+ synchronized (this) {
+ try {
+ long lastHighWaterMark = readProcStatsHighWaterMark(section);
+ List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+ long highWaterMark = mProcessStats.getCommittedStats(
+ lastHighWaterMark, section, true, statsFiles);
+ if (statsFiles.size() != 1) {
+ return;
+ }
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
+ statsFiles.get(0));
+ int[] len = new int[1];
+ byte[] stats = readFully(stream, len);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeStorage(Arrays.copyOf(stats, len[0]));
+ pulledData.add(e);
+ new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
+ + lastHighWaterMark).delete();
+ new File(
+ mBaseDir.getAbsolutePath() + "/" + section + "_"
+ + highWaterMark).createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
}
- InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
- int[] len = new int[1];
- byte[] stats = readFully(stream, len);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeStorage(Arrays.copyOf(stats, len[0]));
- pulledData.add(e);
- new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark).delete();
- new File(
- mBaseDir.getAbsolutePath() + "/" + section + "_"
- + highWaterMark).createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "Getting procstats failed: ", e);
- } catch (RemoteException e) {
- Log.e(TAG, "Getting procstats failed: ", e);
- } catch (SecurityException e) {
- Log.e(TAG, "Getting procstats failed: ", e);
}
}
@@ -1458,6 +1501,10 @@
pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.NATIVE_PROCESS_MEMORY_STATE: {
+ pullNativeProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.BINDER_CALLS: {
pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e5347cf..7f9ee84 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppProtoEnums;
import android.app.IActivityManager;
import android.app.IApplicationThread;
@@ -28,19 +29,26 @@
import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.service.voice.IVoiceInteractionSession;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.ActivityServiceConnectionsHolder;
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.SafeActivityOptions;
import com.android.server.am.TaskRecord;
+import com.android.server.am.UserState;
import com.android.server.am.WindowProcessController;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
/**
* Activity Task manager local system service interface.
@@ -306,13 +314,13 @@
public abstract boolean showStrictModeViolationDialog();
public abstract void showSystemReadyErrorDialogsIfNeeded();
- public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
public abstract void onProcessMapped(int pid, WindowProcessController proc);
public abstract void onProcessUnMapped(int pid);
public abstract void onPackageDataCleared(String name);
public abstract void onPackageUninstalled(String name);
public abstract void onPackageAdded(String name, boolean replacing);
+ public abstract void onPackageReplaced(ApplicationInfo aInfo);
public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai);
@@ -343,4 +351,92 @@
/** @return true if the given process is the factory test process. */
public abstract boolean isFactoryTestProcess(WindowProcessController wpc);
public abstract void updateTopComponentForFactoryTest();
+ public abstract void handleAppDied(WindowProcessController wpc, boolean restarting,
+ Runnable finishInstrumentationCallback);
+ public abstract void closeSystemDialogs(String reason);
+
+ /** Removes all components (e.g. activities, recents, ...) belonging to a disabled package. */
+ public abstract void cleanupDisabledPackageComponents(
+ String packageName, Set<String> disabledClasses, int userId, boolean booted);
+
+ /** Called whenever AM force stops a package. */
+ public abstract boolean onForceStopPackage(String packageName, boolean doit,
+ boolean evenPersistent, int userId);
+ /**
+ * Resumes all top activities in the system if they aren't resumed already.
+ * @param scheduleIdle If the idle message should be schedule after the top activities are
+ * resumed.
+ */
+ public abstract void resumeTopActivities(boolean scheduleIdle);
+
+ /** Called by AM just before it binds to an application process. */
+ public abstract void preBindApplication(WindowProcessController wpc);
+
+ /** Called by AM when an application process attaches. */
+ public abstract boolean attachApplication(WindowProcessController wpc) throws RemoteException;
+
+ /** @see IActivityManager#notifyLockedProfile(int) */
+ public abstract void notifyLockedProfile(@UserIdInt int userId, int currentUserId);
+
+ /** @see IActivityManager#startConfirmDeviceCredentialIntent(Intent, Bundle) */
+ public abstract void startConfirmDeviceCredentialIntent(Intent intent, Bundle options);
+
+ /** Writes current activity states to the proto stream. */
+ public abstract void writeActivitiesToProto(ProtoOutputStream proto);
+
+ /**
+ * Saves the current activity manager state and includes the saved state in the next dump of
+ * activity manager.
+ */
+ public abstract void saveANRState(String reason);
+
+ /** Clears the previously saved activity manager ANR state. */
+ public abstract void clearSavedANRState();
+
+ /** Dump the current state based on the command. */
+ public abstract void dump(String cmd, FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, boolean dumpClient, String dumpPackage);
+
+ /** Dump the current state for inclusion in process dump. */
+ public abstract boolean dumpForProcesses(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ String dumpPackage, int dumpAppId, boolean needSep, boolean testPssMode,
+ int wakefulness);
+
+ /** Writes the current window process states to the proto stream. */
+ public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage);
+
+ /** Dump the current activities state. */
+ public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
+ boolean dumpFocusedStackOnly);
+
+ /** @return true if it the activity management system is okay with GC running now. */
+ public abstract boolean canGcNow();
+
+ /** @return the process for the top-most resumed activity in the system. */
+ public abstract WindowProcessController getTopApp();
+
+ /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
+ public abstract void rankTaskLayersIfNeeded();
+
+ /** Destroy all activities. */
+ public abstract void scheduleDestroyAllActivities(String reason);
+
+ /** Remove user association with activities. */
+ public abstract void removeUser(int userId);
+
+ /** Switch current focused user for activities. */
+ public abstract boolean switchUser(int userId, UserState userState);
+
+ /** Called whenever an app crashes. */
+ public abstract void onHandleAppCrash(WindowProcessController wpc);
+
+ /**
+ * Finish the topmost activities in all stacks that belong to the crashed app.
+ * @param crashedApp The app that crashed.
+ * @param reason Reason to perform this action.
+ * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+ */
+ public abstract int finishTopCrashedActivities(
+ WindowProcessController crashedApp, String reason);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 642f5781..ca23360 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -145,12 +145,14 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.InputChannel;
import android.view.InputDevice;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
+import android.view.WindowManagerPolicyConstants.PointerEventListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
@@ -454,6 +456,8 @@
*/
WindowState mInputMethodWindow;
+ private final PointerEventDispatcher mPointerEventDispatcher;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final AppWindowToken atoken = w.mAppToken;
@@ -833,6 +837,15 @@
mDisplayReady = true;
mInputMonitor = new InputMonitor(service, mDisplayId);
+
+ if (mService.mInputManager != null) {
+ final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+ + mDisplayId, mDisplayId);
+ mPointerEventDispatcher = inputChannel != null
+ ? new PointerEventDispatcher(inputChannel) : null;
+ } else {
+ mPointerEventDispatcher = null;
+ }
}
boolean isReady() {
@@ -1286,6 +1299,19 @@
mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
+
+ // Tap Listeners are supported for:
+ // 1. All physical displays (multi-display).
+ // 2. VirtualDisplays on VR, AA (and everything else).
+ if (mPointerEventDispatcher != null && mTapDetector == null) {
+ if (DEBUG_DISPLAY) {
+ Slog.d(TAG,
+ "Registering PointerEventListener for DisplayId: " + mDisplayId);
+ }
+ mTapDetector = new TaskTapPointerEventListener(mService, this);
+ registerPointerEventListener(mTapDetector);
+ registerPointerEventListener(mService.mMousePositionTracker);
+ }
}
/**
@@ -2077,9 +2103,9 @@
int taskForTapOutside(int x, int y) {
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
- if (stack.isActivityTypeHome()) {
+ if (stack.isActivityTypeHome() && !stack.inMultiWindowMode()) {
// We skip not only home stack, but also everything behind home because user can't
- // see them.
+ // see them when home stack is isn't in multi-window mode.
break;
}
final int taskId = stack.taskIdFromPoint(x, y);
@@ -2186,13 +2212,10 @@
try {
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
- if (mService.canDispatchPointerEvents()) {
- if (mTapDetector != null) {
- mService.unregisterPointerEventListener(mTapDetector);
- }
- if (mDisplayId == DEFAULT_DISPLAY && mService.mMousePositionTracker != null) {
- mService.unregisterPointerEventListener(mService.mMousePositionTracker);
- }
+ if (mPointerEventDispatcher != null && mTapDetector != null) {
+ unregisterPointerEventListener(mTapDetector);
+ unregisterPointerEventListener(mService.mMousePositionTracker);
+ mTapDetector = null;
}
mService.mAnimator.removeDisplayLocked(mDisplayId);
mWindowingLayer.release();
@@ -4409,4 +4432,16 @@
boolean getLastHasContent() {
return mLastHasContent;
}
+
+ void registerPointerEventListener(@NonNull PointerEventListener listener) {
+ if (mPointerEventDispatcher != null) {
+ mPointerEventDispatcher.registerInputEventListener(listener);
+ }
+ }
+
+ void unregisterPointerEventListener(@NonNull PointerEventListener listener) {
+ if (mPointerEventDispatcher != null) {
+ mPointerEventDispatcher.unregisterInputEventListener(listener);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bd82553..847cff9 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -55,6 +55,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
private final WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
private final DisplayPolicy mDisplayPolicy;
private final Context mContext;
private final Object mLock;
@@ -106,6 +107,7 @@
DisplayRotation(WindowManagerService service, DisplayContent displayContent,
DisplayPolicy displayPolicy, Context context, Object lock) {
mService = service;
+ mDisplayContent = displayContent;
mDisplayPolicy = displayPolicy;
mContext = context;
mLock = lock;
@@ -225,6 +227,70 @@
}
}
+ void restoreUserRotation(int userRotationMode, int userRotation) {
+ if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
+ && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
+ Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
+ + " for " + mDisplayContent);
+ userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+ }
+ if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
+ Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
+ + " for " + mDisplayContent);
+ userRotation = Surface.ROTATION_0;
+ }
+ mUserRotationMode = userRotationMode;
+ mUserRotation = userRotation;
+ }
+
+ private void setUserRotation(int userRotationMode, int userRotation) {
+ if (isDefaultDisplay) {
+ // We'll be notified via settings listener, so we don't need to update internal values.
+ final ContentResolver res = mContext.getContentResolver();
+ final int accelerometerRotation =
+ userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
+ Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
+ accelerometerRotation, UserHandle.USER_CURRENT);
+ Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
+ UserHandle.USER_CURRENT);
+ return;
+ }
+
+ boolean changed = false;
+ if (mUserRotationMode != userRotationMode) {
+ mUserRotationMode = userRotationMode;
+ changed = true;
+ }
+ if (mUserRotation != userRotation) {
+ mUserRotation = userRotation;
+ changed = true;
+ }
+ mService.mDisplaySettings.setUserRotation(mDisplayContent, userRotationMode, userRotation);
+ if (changed) {
+ mService.updateRotation(true /* alwaysSendConfiguration */,
+ false /* forceRelayout */);
+ mService.mDisplaySettings.writeSettingsLocked();
+ }
+ }
+
+ void freezeRotation(int rotation) {
+ rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation;
+ setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
+ }
+
+ void thawRotation() {
+ setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
+ }
+
+ boolean isRotationFrozen() {
+ if (!isDefaultDisplay) {
+ return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
+ }
+
+ return Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
+ }
+
/** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
boolean isDefaultOrientationForced() {
return mForceDefaultOrientation;
@@ -381,9 +447,6 @@
* @param orientation An orientation constant, such as
* {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
* @param lastRotation The most recently used rotation.
- * @param defaultDisplay Flag indicating whether the rotation is computed for the default
- * display. Currently for all non-default displays sensors, docking mode,
- * rotation lock and other factors are ignored.
* @return The surface rotation to use.
*/
int rotationForOrientation(int orientation, int lastRotation) {
@@ -418,8 +481,8 @@
final int preferredRotation;
if (!isDefaultDisplay) {
// For secondary displays we ignore things like displays sensors, docking mode and
- // rotation lock, and always prefer a default rotation.
- preferredRotation = Surface.ROTATION_0;
+ // rotation lock, and always prefer user rotation.
+ preferredRotation = mUserRotation;
} else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
// Ignore sensor when lid switch is open and rotation is forced.
preferredRotation = mLidOpenRotation;
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index bbb690f..28dc008 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -20,20 +20,23 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.WindowConfiguration;
-import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Environment;
-import android.provider.Settings;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.policy.WindowManagerPolicy;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -43,10 +46,6 @@
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
/**
* Current persistent settings about a display
*/
@@ -58,15 +57,25 @@
private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
private static class Entry {
- private final String name;
- private int overscanLeft;
- private int overscanTop;
- private int overscanRight;
- private int overscanBottom;
- private int windowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+ private final String mName;
+ private int mOverscanLeft;
+ private int mOverscanTop;
+ private int mOverscanRight;
+ private int mOverscanBottom;
+ private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+ private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+ private int mUserRotation = Surface.ROTATION_0;
private Entry(String _name) {
- name = _name;
+ mName = _name;
+ }
+
+ private boolean isEmpty() {
+ return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
+ && mOverscanBottom == 0
+ && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
+ && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+ && mUserRotation == Surface.ROTATION_0;
}
}
@@ -90,13 +99,30 @@
return entry;
}
+ private Entry getOrCreateEntry(String uniqueId, String name) {
+ Entry entry = getEntry(uniqueId, name);
+ if (entry == null) {
+ entry = new Entry(uniqueId);
+ mEntries.put(uniqueId, entry);
+ }
+ return entry;
+ }
+
+ private void removeEntryIfEmpty(String uniqueId, String name) {
+ final Entry entry = getEntry(uniqueId, name);
+ if (entry.isEmpty()) {
+ mEntries.remove(uniqueId);
+ mEntries.remove(name);
+ }
+ }
+
private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
final Entry entry = getEntry(name, uniqueId);
if (entry != null) {
- outRect.left = entry.overscanLeft;
- outRect.top = entry.overscanTop;
- outRect.right = entry.overscanRight;
- outRect.bottom = entry.overscanBottom;
+ outRect.left = entry.mOverscanLeft;
+ outRect.top = entry.mOverscanTop;
+ outRect.right = entry.mOverscanRight;
+ outRect.bottom = entry.mOverscanBottom;
} else {
outRect.set(0, 0, 0, 0);
}
@@ -104,28 +130,22 @@
void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
int bottom) {
- if (left == 0 && top == 0 && right == 0 && bottom == 0) {
- // Right now all we are storing is overscan; if there is no overscan,
- // we have no need for the entry.
- mEntries.remove(uniqueId);
- // Legacy name might have been in used, so we need to clear it.
- mEntries.remove(name);
+ Entry entry = mEntries.get(uniqueId);
+ if (left == 0 && top == 0 && right == 0 && bottom == 0 && entry == null) {
+ // All default value, no action needed.
return;
}
- Entry entry = mEntries.get(uniqueId);
- if (entry == null) {
- entry = new Entry(uniqueId);
- mEntries.put(uniqueId, entry);
- }
- entry.overscanLeft = left;
- entry.overscanTop = top;
- entry.overscanRight = right;
- entry.overscanBottom = bottom;
+ entry = getOrCreateEntry(uniqueId, name);
+ entry.mOverscanLeft = left;
+ entry.mOverscanTop = top;
+ entry.mOverscanRight = right;
+ entry.mOverscanBottom = bottom;
+ removeEntryIfEmpty(uniqueId, name);
}
private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
final Entry entry = getEntry(name, uniqueId);
- int windowingMode = entry != null ? entry.windowingMode
+ int windowingMode = entry != null ? entry.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
// This display used to be in freeform, but we don't support freeform anymore, so fall
// back to fullscreen.
@@ -148,6 +168,36 @@
return windowingMode;
}
+ void setUserRotation(DisplayContent dc, int rotationMode, int rotation) {
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+
+ final String uniqueId = displayInfo.uniqueId;
+ final String name = displayInfo.name;
+ Entry entry = getEntry(displayInfo.name, uniqueId);
+ if (rotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+ && rotation == Surface.ROTATION_0 && entry == null) {
+ // All default values. No action needed.
+ return;
+ }
+
+ entry = getOrCreateEntry(uniqueId, name);
+ entry.mUserRotationMode = rotationMode;
+ entry.mUserRotation = rotation;
+ removeEntryIfEmpty(uniqueId, name);
+ }
+
+ private void restoreUserRotation(DisplayContent dc) {
+ final DisplayInfo info = dc.getDisplayInfo();
+
+ final Entry entry = getEntry(info.name, info.uniqueId);
+ final int userRotationMode = entry != null ? entry.mUserRotationMode
+ : WindowManagerPolicy.USER_ROTATION_FREE;
+ final int userRotation = entry != null ? entry.mUserRotation
+ : Surface.ROTATION_0;
+
+ dc.getDisplayRotation().restoreUserRotation(userRotationMode, userRotation);
+ }
+
void applySettingsToDisplayLocked(DisplayContent dc) {
final DisplayInfo displayInfo = dc.getDisplayInfo();
@@ -161,6 +211,8 @@
displayInfo.overscanTop = rect.top;
displayInfo.overscanRight = rect.right;
displayInfo.overscanBottom = rect.bottom;
+
+ restoreUserRotation(dc);
}
void readSettingsLocked() {
@@ -244,12 +296,16 @@
String name = parser.getAttributeValue(null, "name");
if (name != null) {
Entry entry = new Entry(name);
- entry.overscanLeft = getIntAttribute(parser, "overscanLeft");
- entry.overscanTop = getIntAttribute(parser, "overscanTop");
- entry.overscanRight = getIntAttribute(parser, "overscanRight");
- entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
- entry.windowingMode = getIntAttribute(parser, "windowingMode",
+ entry.mOverscanLeft = getIntAttribute(parser, "overscanLeft");
+ entry.mOverscanTop = getIntAttribute(parser, "overscanTop");
+ entry.mOverscanRight = getIntAttribute(parser, "overscanRight");
+ entry.mOverscanBottom = getIntAttribute(parser, "overscanBottom");
+ entry.mWindowingMode = getIntAttribute(parser, "windowingMode",
WindowConfiguration.WINDOWING_MODE_UNDEFINED);
+ entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode",
+ WindowManagerPolicy.USER_ROTATION_FREE);
+ entry.mUserRotation = getIntAttribute(parser, "userRotation",
+ Surface.ROTATION_0);
mEntries.put(name, entry);
}
XmlUtils.skipCurrentTag(parser);
@@ -272,21 +328,28 @@
for (Entry entry : mEntries.values()) {
out.startTag(null, "display");
- out.attribute(null, "name", entry.name);
- if (entry.overscanLeft != 0) {
- out.attribute(null, "overscanLeft", Integer.toString(entry.overscanLeft));
+ out.attribute(null, "name", entry.mName);
+ if (entry.mOverscanLeft != 0) {
+ out.attribute(null, "overscanLeft", Integer.toString(entry.mOverscanLeft));
}
- if (entry.overscanTop != 0) {
- out.attribute(null, "overscanTop", Integer.toString(entry.overscanTop));
+ if (entry.mOverscanTop != 0) {
+ out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop));
}
- if (entry.overscanRight != 0) {
- out.attribute(null, "overscanRight", Integer.toString(entry.overscanRight));
+ if (entry.mOverscanRight != 0) {
+ out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight));
}
- if (entry.overscanBottom != 0) {
- out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
+ if (entry.mOverscanBottom != 0) {
+ out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom));
}
- if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode));
+ if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode));
+ }
+ if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) {
+ out.attribute(null, "userRotationMode",
+ Integer.toString(entry.mUserRotationMode));
+ }
+ if (entry.mUserRotation != Surface.ROTATION_0) {
+ out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
}
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index b5e2f01..10d77e5 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -108,7 +108,7 @@
}
// All the calls below need to happen without the WM lock held since they call into AM.
- mService.mAmInternal.saveANRState(reason);
+ mService.mAtmInternal.saveANRState(reason);
if (appWindowToken != null && appWindowToken.appToken != null) {
// Notify the activity manager about the timeout and let it decide whether
@@ -125,7 +125,7 @@
} else if (windowState != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- long timeout = mService.mAtmInternal.inputDispatchingTimedOut(
+ long timeout = mService.mAmInternal.inputDispatchingTimedOut(
windowState.mSession.mPid, aboveSystem, reason);
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 15f6938..ed3e6c6 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -435,14 +435,14 @@
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
if (recentsAnimationController != null
- && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+ && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
if (recentsAnimationController.updateInputConsumerForApp(
recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
mAddRecentsAnimationInputConsumerHandle = false;
}
- // Skip adding the window below regardless of whether there is an input consumer
- // to handle it
+ // If the target app window does not yet exist, then we don't add the input
+ // consumer window, but also don't add the app window below.
return;
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6fef1630..5c80759 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -458,10 +458,9 @@
mRunner = null;
mCanceled = true;
- // Clear associated input consumers
+ // Update the input windows after the animation is complete
final InputMonitor inputMonitor =
mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
- inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
inputMonitor.updateInputWindowsLw(true /*force*/);
// We have deferred all notifications to the target app as a part of the recents animation,
@@ -494,6 +493,11 @@
@Override
public void binderDied() {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
+
+ // Clear associated input consumers on runner death
+ final InputMonitor inputMonitor =
+ mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+ inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -516,8 +520,14 @@
&& isTargetOverWallpaper();
}
- boolean hasInputConsumerForApp(AppWindowToken appToken) {
- return mInputConsumerEnabled && isAnimatingApp(appToken);
+ /**
+ * @return Whether to use the input consumer to override app input to route home/recents.
+ */
+ boolean shouldApplyInputConsumer(AppWindowToken appToken) {
+ // Only apply the input consumer if it is enabled, it is not the target (home/recents)
+ // being revealed with the transition, and we are actively animating the app as a part of
+ // the animation
+ return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken);
}
boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
@@ -675,6 +685,7 @@
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+ pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3fef87d..c8977be 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -228,21 +228,6 @@
mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayId, dc.getDisplayInfo());
dc.configureDisplayPolicy();
-
- // Tap Listeners are supported for:
- // 1. All physical displays (multi-display).
- // 2. VirtualDisplays on VR, AA (and everything else).
- if (mService.canDispatchPointerEvents()) {
- if (DEBUG_DISPLAY) {
- Slog.d(TAG,
- "Registering PointerEventListener for DisplayId: " + displayId);
- }
- dc.mTapDetector = new TaskTapPointerEventListener(mService, dc);
- mService.registerPointerEventListener(dc.mTapDetector);
- if (displayId == DEFAULT_DISPLAY) {
- mService.registerPointerEventListener(mService.mMousePositionTracker);
- }
- }
}
return dc;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 21e807e..6fd17955 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.graphics.Bitmap.CompressFormat.*;
+
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -361,6 +362,7 @@
// For snapshots with reduced resolution, do not create or save full sized bitmaps
if (mSnapshot.isReducedResolution()) {
+ swBitmap.recycle();
return true;
}
@@ -373,6 +375,8 @@
Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
return false;
}
+ reduced.recycle();
+ swBitmap.recycle();
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 5410676..b096bf2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -430,14 +430,25 @@
public abstract boolean isUidFocused(int uid);
/**
- * Checks whether the specified process has IME focus or not.
+ * Checks whether the specified IME client has IME focus or not.
*
* @param uid UID of the process to be queried
* @param pid PID of the process to be queried
- * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
- * focus
+ * @param displayId Display ID reported from the client. Note that this method also verifies
+ * whether the specified process is allowed to access to this display or not
+ * @return {@code true} if the IME client specified with {@code uid}, {@code pid}, and
+ * {@code displayId} has IME focus
*/
- public abstract boolean isInputMethodClientFocus(int uid, int pid);
+ public abstract boolean isInputMethodClientFocus(int uid, int pid, int displayId);
+
+ /**
+ * Checks whether the given {@code uid} is allowed to use the given {@code displayId} or not.
+ *
+ * @param displayId Display ID to be checked
+ * @param uid UID to be checked.
+ * @return {@code true} if the given {@code uid} is allowed to use the given {@code displayId}
+ */
+ public abstract boolean isUidAllowedOnDisplay(int displayId, int uid);
/**
* Return the display Id for given window.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 10ba63e..5642b1f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -45,8 +45,8 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -753,8 +753,6 @@
final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>();
final BoundsAnimationController mBoundsAnimationController;
- private final PointerEventDispatcher mPointerEventDispatcher;
-
private WindowContentFrameStats mTempWindowRenderStats;
private final LatencyTracker mLatencyTracker;
@@ -945,14 +943,6 @@
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
- if(mInputManager != null) {
- final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
- mPointerEventDispatcher = inputChannel != null
- ? new PointerEventDispatcher(inputChannel) : null;
- } else {
- mPointerEventDispatcher = null;
- }
-
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
@@ -1928,7 +1918,7 @@
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
- if ((flagChanges & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
+ if ((flagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
updateNonSystemOverlayWindowsVisibilityIfNeeded(
win, win.mWinAnimator.getShown());
}
@@ -2685,8 +2675,9 @@
public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
synchronized (mWindowMap) {
if (mRecentsAnimationController != null) {
- mRecentsAnimationController.cleanupAnimation(reorderMode);
+ final RecentsAnimationController controller = mRecentsAnimationController;
mRecentsAnimationController = null;
+ controller.cleanupAnimation(reorderMode);
mAppTransition.updateBooster();
}
}
@@ -3126,18 +3117,23 @@
}
@Override
- public void registerPointerEventListener(PointerEventListener listener) {
- mPointerEventDispatcher.registerInputEventListener(listener);
+ public void registerPointerEventListener(PointerEventListener listener, int displayId) {
+ synchronized (mWindowMap) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.registerPointerEventListener(listener);
+ }
+ }
}
@Override
- public void unregisterPointerEventListener(PointerEventListener listener) {
- mPointerEventDispatcher.unregisterInputEventListener(listener);
- }
-
- /** Check if the service is set to dispatch pointer events. */
- boolean canDispatchPointerEvents() {
- return mPointerEventDispatcher != null;
+ public void unregisterPointerEventListener(PointerEventListener listener, int displayId) {
+ synchronized (mWindowMap) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.unregisterPointerEventListener(listener);
+ }
+ }
}
// Called by window manager policy. Not exposed externally.
@@ -3672,14 +3668,19 @@
}
}
+ @Override
+ public void freezeRotation(int rotation) {
+ freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
+ }
+
/**
* Freeze rotation changes. (Enable "rotation lock".)
* Persists across reboots.
- * @param rotation The desired rotation to freeze to, or -1 to use the
- * current rotation.
+ * @param displayId The ID of the display to freeze.
+ * @param rotation The desired rotation to freeze to, or -1 to use the current rotation.
*/
@Override
- public void freezeRotation(int rotation) {
+ public void freezeDisplayRotation(int displayId, int rotation) {
// TODO(multi-display): Track which display is rotated.
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"freezeRotation()")) {
@@ -3690,14 +3691,16 @@
+ "rotation constant.");
}
- final int defaultDisplayRotation = getDefaultDisplayRotation();
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation="
- + defaultDisplayRotation);
-
long origId = Binder.clearCallingIdentity();
try {
- mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
- rotation == -1 ? defaultDisplayRotation : rotation);
+ synchronized (mWindowMap) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to freeze rotation for a missing display.");
+ return;
+ }
+ display.getDisplayRotation().freezeRotation(rotation);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3705,12 +3708,17 @@
updateRotationUnchecked(false, false);
}
+ @Override
+ public void thawRotation() {
+ thawDisplayRotation(Display.DEFAULT_DISPLAY);
+ }
+
/**
* Thaw rotation changes. (Disable "rotation lock".)
* Persists across reboots.
*/
@Override
- public void thawRotation() {
+ public void thawDisplayRotation(int displayId) {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"thawRotation()")) {
throw new SecurityException("Requires SET_ORIENTATION permission");
@@ -3721,8 +3729,14 @@
long origId = Binder.clearCallingIdentity();
try {
- mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE,
- 777); // rot not used
+ synchronized (mWindowMap) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+ return;
+ }
+ display.getDisplayRotation().thawRotation();
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3730,6 +3744,23 @@
updateRotationUnchecked(false, false);
}
+ @Override
+ public boolean isRotationFrozen() {
+ return isDisplayRotationFrozen(Display.DEFAULT_DISPLAY);
+ }
+
+ @Override
+ public boolean isDisplayRotationFrozen(int displayId) {
+ synchronized (mWindowMap) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+ return false;
+ }
+ return display.getDisplayRotation().isRotationFrozen();
+ }
+ }
+
/**
* Recalculate the current rotation.
*
@@ -3799,11 +3830,6 @@
}
@Override
- public boolean isRotationFrozen() {
- return mPolicy.getUserRotationMode() == WindowManagerPolicy.USER_ROTATION_LOCKED;
- }
-
- @Override
public int watchRotation(IRotationWatcher watcher, int displayId) {
final DisplayContent displayContent;
synchronized (mWindowMap) {
@@ -4816,7 +4842,7 @@
synchronized (mWindowMap) {
mLastANRState = null;
}
- mAmInternal.clearSavedANRState();
+ mAtmInternal.clearSavedANRState();
}
break;
case WALLPAPER_DRAW_PENDING_TIMEOUT: {
@@ -7201,16 +7227,20 @@
}
@Override
- public boolean isInputMethodClientFocus(int uid, int pid) {
+ public boolean isInputMethodClientFocus(int uid, int pid, int displayId) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ return false;
+ }
synchronized (mWindowMap) {
- // Check all displays if any input method window has focus.
- for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mRoot.mChildren.get(i);
- if (displayContent.isInputMethodClientFocus(uid, pid)) {
- return true;
- }
+ final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
+ if (displayContent == null
+ || displayContent.getDisplayId() != displayId
+ || !displayContent.hasAccess(uid)) {
+ return false;
}
-
+ if (displayContent.isInputMethodClientFocus(uid, pid)) {
+ return true;
+ }
// Okay, how about this... what is the current focus?
// It seems in some cases we may not have moved the IM
// target window, such as when it was in a pop-up window,
@@ -7219,7 +7249,7 @@
// press home. Sometimes the IME won't go down.)
// Would be nice to fix this more correctly, but it's
// way at the end of a release, and this should be good enough.
- final WindowState currentFocus = mRoot.getTopFocusedDisplayContent().mCurrentFocus;
+ final WindowState currentFocus = displayContent.mCurrentFocus;
if (currentFocus != null && currentFocus.mSession.mUid == uid
&& currentFocus.mSession.mPid == pid) {
return true;
@@ -7229,6 +7259,20 @@
}
@Override
+ public boolean isUidAllowedOnDisplay(int displayId, int uid) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return true;
+ }
+ if (displayId == Display.INVALID_DISPLAY) {
+ return false;
+ }
+ synchronized (mWindowMap) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ return displayContent != null && displayContent.hasAccess(uid);
+ }
+ }
+
+ @Override
public int getDisplayIdForWindow(IBinder windowToken) {
synchronized (mWindowMap) {
final WindowState window = mWindowMap.get(windowToken);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 831418b..bf2d0df 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -26,6 +26,7 @@
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.IWindowManager;
+import android.view.Surface;
import java.io.PrintWriter;
import java.util.regex.Matcher;
@@ -73,6 +74,8 @@
// trace files can be written.
return mInternal.mWindowTracing.onShellCommand(this,
getNextArgRequired());
+ case "set-user-rotation":
+ return runSetDisplayUserRotation(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -262,6 +265,36 @@
return Integer.parseInt(s);
}
+ private int runSetDisplayUserRotation(PrintWriter pw) {
+ final String lockMode = getNextArgRequired();
+
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArg();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArg();
+ }
+
+ if ("free".equals(lockMode)) {
+ mInternal.thawDisplayRotation(displayId);
+ return 0;
+ }
+
+ if (!lockMode.equals("lock")) {
+ getErrPrintWriter().println("Error: lock mode needs to be either free or lock.");
+ return -1;
+ }
+
+ try {
+ final int rotation = arg != null ? Integer.parseInt(arg) : Surface.ROTATION_0;
+ mInternal.freezeDisplayRotation(displayId, rotation);
+ return 0;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ return -1;
+ }
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -279,6 +312,8 @@
pw.println(" Set display scaling mode.");
pw.println(" dismiss-keyguard");
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
+ pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
+ pw.println(" Set user rotation mode and user rotation.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8276952..eacbda1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -46,12 +46,12 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -3470,7 +3470,7 @@
* this window is visible.
*/
boolean hideNonSystemOverlayWindowsWhenVisible() {
- return (mAttrs.privateFlags & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0
+ return (mAttrs.privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0
&& mSession.mCanHideNonSystemOverlayWindows;
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 045f4eb..15a3a1a3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -99,6 +99,7 @@
"libutils",
"libhwui",
"libbpf",
+ "libnetdbpf",
"libnetdutils",
"android.hardware.audio.common@2.0",
"android.hardware.broadcastradio@1.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c66d03c..3943dba 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -217,7 +217,7 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+ const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
@@ -442,11 +442,11 @@
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+ const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle,
+ int32_t displayId) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputChannel(
- inputChannel, inputWindowHandle, monitor);
+ return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle,
+ displayId);
}
status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
@@ -1316,7 +1316,7 @@
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
+ jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jint displayId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1330,7 +1330,7 @@
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
- env, inputChannel, inputWindowHandle, monitor);
+ env, inputChannel, inputWindowHandle, displayId);
if (status) {
std::string message;
message += StringPrintf("Failed to register input channel. status=%d", status);
@@ -1338,7 +1338,8 @@
return;
}
- if (! monitor) {
+ // If inputWindowHandle is null and displayId >= 0, treat inputChannel as monitor.
+ if (inputWindowHandle != nullptr || displayId == ADISPLAY_ID_NONE) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
@@ -1639,7 +1640,7 @@
{ "nativeHasKeys", "(JII[I[Z)Z",
(void*) nativeHasKeys },
{ "nativeRegisterInputChannel",
- "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;Z)V",
+ "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;I)V",
(void*) nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
(void*) nativeUnregisterInputChannel },
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 3302dea..649f1a5 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -30,8 +30,8 @@
#include <utils/Log.h>
#include "android-base/unique_fd.h"
-#include "bpf/BpfNetworkStats.h"
#include "bpf/BpfUtils.h"
+#include "netdbpf/BpfNetworkStats.h"
using android::bpf::Stats;
using android::bpf::hasBpfSupport;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 4350596..2dbbf55 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -15,7 +15,9 @@
*/
package com.android.server.devicepolicy;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
+import android.content.ComponentName;
import com.android.server.SystemService;
@@ -68,7 +70,22 @@
}
@Override
- public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) {
+ public boolean checkDeviceIdentifierAccess(String packageName, int userHandle, int pid,
+ int uid) {
return false;
}
+
+ @Override
+ public void setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
+ }
+
+ @Override
+ public int getGlobalPrivateDnsMode(ComponentName who) {
+ return DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
+ }
+
+ @Override
+ public String getGlobalPrivateDnsHost(ComponentName who) {
+ return null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 913b844..b88165e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,12 +55,18 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
@@ -145,6 +151,7 @@
import android.media.IAudioService;
import android.net.ConnectivityManager;
import android.net.IIpConnectivityMetrics;
+import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.Uri;
import android.net.metrics.IpConnectivityLog;
@@ -199,6 +206,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsLog;
import android.util.Xml;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -395,6 +403,8 @@
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_SLEEP_POLICY);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
+ GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_MODE);
+ GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>();
GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON);
@@ -7862,7 +7872,21 @@
}
@Override
- public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) {
+ public boolean checkDeviceIdentifierAccess(String packageName, int userHandle, int pid,
+ int uid) {
+ // If the caller is not a system app then it should only be able to check its own device
+ // identifier access.
+ int callingAppId = UserHandle.getAppId(mInjector.binderGetCallingUid());
+ if (callingAppId >= Process.FIRST_APPLICATION_UID
+ && callingAppId != UserHandle.getAppId(uid)) {
+ return false;
+ }
+ // A device or profile owner must also have the READ_PHONE_STATE permission to access device
+ // identifiers. If the package being checked does not have this permission then deny access.
+ if (mContext.checkPermission(android.Manifest.permission.READ_PHONE_STATE, pid, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
// Allow access to the device owner.
ComponentName deviceOwner = getDeviceOwnerComponent(true);
if (deviceOwner != null && deviceOwner.getPackageName().equals(packageName)) {
@@ -9364,6 +9388,7 @@
}
saveUserRestrictionsLocked(userHandle);
}
+ StatsLog.write(StatsLog.USER_RESTRICTION_CHANGED, key, enabledFromThisOwner);
if (SecurityLog.isLoggingEnabled()) {
final int eventTag = enabledFromThisOwner
? SecurityLog.TAG_USER_RESTRICTION_ADDED
@@ -13114,4 +13139,78 @@
private static String getManagedProvisioningPackage(Context context) {
return context.getResources().getString(R.string.config_managed_provisioning_package);
}
+
+ private void putPrivateDnsSettings(@Nullable String mode, @Nullable String host) {
+ // Set Private DNS settings using system permissions, as apps cannot write
+ // to global settings.
+ long origId = mInjector.binderClearCallingIdentity();
+ try {
+ mInjector.settingsGlobalPutString(PRIVATE_DNS_MODE, mode);
+ mInjector.settingsGlobalPutString(PRIVATE_DNS_SPECIFIER, host);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void setGlobalPrivateDns(@NonNull ComponentName who, int mode, String privateDnsHost) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ enforceDeviceOwner(who);
+
+ switch (mode) {
+ case PRIVATE_DNS_MODE_OPPORTUNISTIC:
+ if (!TextUtils.isEmpty(privateDnsHost)) {
+ throw new IllegalArgumentException("A DNS host should not be provided when " +
+ "setting opportunistic mode.");
+ }
+ putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, null);
+ break;
+ case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+ if (!NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+ throw new IllegalArgumentException(
+ String.format("Provided hostname is not valid: %s", privateDnsHost));
+ }
+ putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+ privateDnsHost);
+ break;
+ default:
+ throw new IllegalArgumentException(String.format("Unsupported mode: %d", mode));
+ }
+ }
+
+ @Override
+ public int getGlobalPrivateDnsMode(@NonNull ComponentName who) {
+ if (!mHasFeature) {
+ return PRIVATE_DNS_MODE_UNKNOWN;
+ }
+
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ enforceDeviceOwner(who);
+ switch (mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE)) {
+ case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
+ return PRIVATE_DNS_MODE_OFF;
+ case ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC:
+ return PRIVATE_DNS_MODE_OPPORTUNISTIC;
+ case ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+ return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+ }
+
+ return PRIVATE_DNS_MODE_UNKNOWN;
+ }
+
+ @Override
+ public String getGlobalPrivateDnsHost(@NonNull ComponentName who) {
+ if (!mHasFeature) {
+ return null;
+ }
+
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ enforceDeviceOwner(who);
+
+ return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
+ }
}
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 78c0be4..e67f8d3 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -84,7 +84,7 @@
LOCAL_JAVA_LIBRARIES := \
junit \
- platform-robolectric-3.6.1-prebuilt
+ platform-robolectric-3.6.2-prebuilt
LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
LOCAL_MODULE := FrameworksServicesRoboTests
@@ -109,4 +109,4 @@
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
-include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
+include prebuilts/misc/common/robolectric/3.6.2/run_robotests.mk
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
index 8c02833..e6cbb5b 100644
--- a/services/tests/mockingservicestests/Android.mk
+++ b/services/tests/mockingservicestests/Android.mk
@@ -22,7 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
frameworks-base-testutils \
services.core \
- androidx-test \
+ services.net \
+ androidx.test.runner \
mockito-target-extended-minus-junit4 \
platform-test-annotations \
ShortcutManagerTestUtils \
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index de3d285..877c8fa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -58,6 +58,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.annotations.GuardedBy;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -67,8 +69,6 @@
import org.mockito.Mock;
import org.mockito.MockitoSession;
-import javax.annotation.concurrent.GuardedBy;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AlarmManagerServiceTest {
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 95ed00f..8afd788 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -41,6 +41,9 @@
import static com.android.server.DeviceIdleController.stateToString;
import static org.junit.Assert.assertEquals;
+
+import android.net.NetworkInfo;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -50,6 +53,8 @@
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
+import android.net.ConnectivityManager;
+import android.content.Intent;
import android.app.IActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -83,11 +88,14 @@
private DeviceIdleController mDeviceIdleController;
private AnyMotionDetectorForTest mAnyMotionDetector;
private AppStateTrackerForTest mAppStateTracker;
+ private InjectorForTest mInjector;
private MockitoSession mMockingSession;
@Mock
private AlarmManager mAlarmManager;
@Mock
+ private ConnectivityService mConnectivityService;
+ @Mock
private DeviceIdleController.Constants mConstants;
@Mock
private IActivityManager mIActivityManager;
@@ -99,6 +107,8 @@
private PowerManager.WakeLock mWakeLock;
class InjectorForTest extends DeviceIdleController.Injector {
+ ConnectivityService connectivityService;
+ LocationManager locationManager;
InjectorForTest(Context ctx) {
super(ctx);
@@ -122,18 +132,19 @@
@Override
ConnectivityService getConnectivityService() {
- return null;
+ return connectivityService;
}
@Override
- DeviceIdleController.Constants getConstants(DeviceIdleController controller, Handler handler,
+ DeviceIdleController.Constants getConstants(DeviceIdleController controller,
+ Handler handler,
ContentResolver resolver) {
return mConstants;
}
@Override
LocationManager getLocationManager() {
- return mLocationManager;
+ return locationManager;
}
@Override
@@ -201,8 +212,8 @@
doNothing().when(mWakeLock).acquire();
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
- mDeviceIdleController = new DeviceIdleController(getContext(),
- new InjectorForTest(getContext()));
+ mInjector = new InjectorForTest(getContext());
+ mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
spyOn(mDeviceIdleController);
doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
mDeviceIdleController.onStart();
@@ -271,6 +282,60 @@
}
@Test
+ public void testUpdateConnectivityState() {
+ // No connectivity service
+ final boolean isConnected = mDeviceIdleController.isNetworkConnected();
+ mInjector.connectivityService = null;
+ mDeviceIdleController.updateConnectivityState(null);
+ assertEquals(isConnected, mDeviceIdleController.isNetworkConnected());
+
+ // No active network info
+ mInjector.connectivityService = mConnectivityService;
+ doReturn(null).when(mConnectivityService).getActiveNetworkInfo();
+ mDeviceIdleController.updateConnectivityState(null);
+ assertFalse(mDeviceIdleController.isNetworkConnected());
+
+ // Active network info says connected.
+ final NetworkInfo ani = mock(NetworkInfo.class);
+ doReturn(ani).when(mConnectivityService).getActiveNetworkInfo();
+ doReturn(true).when(ani).isConnected();
+ mDeviceIdleController.updateConnectivityState(null);
+ assertTrue(mDeviceIdleController.isNetworkConnected());
+
+ // Active network info says not connected.
+ doReturn(false).when(ani).isConnected();
+ mDeviceIdleController.updateConnectivityState(null);
+ assertFalse(mDeviceIdleController.isNetworkConnected());
+
+ // Wrong intent passed (false).
+ Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 3);
+ doReturn(true).when(ani).isConnected();
+ doReturn(1).when(ani).getType();
+ mDeviceIdleController.updateConnectivityState(intent);
+ // Wrong intent means we shouldn't update the connected state.
+ assertFalse(mDeviceIdleController.isNetworkConnected());
+
+ // Intent says connected.
+ doReturn(1).when(ani).getType();
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 1);
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ mDeviceIdleController.updateConnectivityState(intent);
+ assertTrue(mDeviceIdleController.isNetworkConnected());
+
+ // Wrong intent passed (true).
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 3);
+ // Wrong intent means we shouldn't update the connected state.
+ assertTrue(mDeviceIdleController.isNetworkConnected());
+
+ // Intent says not connected.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 1);
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+ mDeviceIdleController.updateConnectivityState(intent);
+ assertFalse(mDeviceIdleController.isNetworkConnected());
+ }
+
+ @Test
public void testStateActiveToStateInactive_ConditionsNotMet() {
mDeviceIdleController.becomeActiveLocked("testing", 0);
verifyStateConditions(STATE_ACTIVE);
@@ -360,8 +425,56 @@
}
@Test
+ public void testStepIdleStateLocked_ValidStates_WithWakeFromIdleAlarmSoon() {
+ enterDeepState(STATE_ACTIVE);
+ // Return that there's an alarm coming soon.
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_ACTIVE);
+
+ // Everything besides ACTIVE should end up as INACTIVE since the screen would be off.
+
+ enterDeepState(STATE_INACTIVE);
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_INACTIVE);
+
+ enterDeepState(STATE_IDLE_PENDING);
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_INACTIVE);
+
+ enterDeepState(STATE_SENSING);
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_INACTIVE);
+
+ enterDeepState(STATE_LOCATING);
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_INACTIVE);
+
+ enterDeepState(STATE_IDLE);
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_INACTIVE);
+
+ enterDeepState(STATE_IDLE_MAINTENANCE);
+ doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+ mAlarmManager).getNextWakeFromIdleTime();
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_INACTIVE);
+ }
+
+ @Test
public void testStepIdleStateLocked_ValidStates_NoLocationManager() {
- mDeviceIdleController.setLocationManagerForTest(null);
+ mInjector.locationManager = null;
// Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
// Set state to INACTIVE.
@@ -427,9 +540,9 @@
@Test
public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
+ mInjector.locationManager = mLocationManager;
doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
// Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
- // TODO: add tests for when there's a wake-from-idle alarm coming soon.
doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
// Set state to INACTIVE.
mDeviceIdleController.becomeActiveLocked("testing", 0);
@@ -463,6 +576,160 @@
}
@Test
+ public void testLightStepIdleStateLocked_InvalidStates() {
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ // stepLightIdleStateLocked doesn't handle the ACTIVE case, so the state
+ // should stay as ACTIVE.
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ }
+
+ /**
+ * Make sure stepLightIdleStateLocked doesn't change state when the state is
+ * LIGHT_STATE_OVERRIDE.
+ */
+ @Test
+ public void testLightStepIdleStateLocked_Overriden() {
+ enterLightState(LIGHT_STATE_OVERRIDE);
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
+ }
+
+ @Test
+ public void testLightStepIdleStateLocked_ValidStates_NoActiveOps_NetworkConnected() {
+ setNetworkConnected(true);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+ // No active ops means INACTIVE should go straight to IDLE.
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testLightStepIdleStateLocked_ValidStates_ActiveOps_NetworkConnected() {
+ setNetworkConnected(true);
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+ // Active ops means INACTIVE should go to PRE_IDLE to wait.
+ mDeviceIdleController.setJobsActive(true);
+ mDeviceIdleController.setAlarmsActive(true);
+ mDeviceIdleController.setActiveIdleOpsForTest(1);
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
+
+ // Even with active ops, PRE_IDLE should go to IDLE.
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testLightStepIdleStateLocked_ValidStates_NoActiveOps_NoNetworkConnected() {
+ setNetworkConnected(false);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+ // No active ops means INACTIVE should go straight to IDLE.
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ // Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testLightStepIdleStateLocked_ValidStates_ActiveOps_NoNetworkConnected() {
+ setNetworkConnected(false);
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+ // Active ops means INACTIVE should go to PRE_IDLE to wait.
+ mDeviceIdleController.setJobsActive(true);
+ mDeviceIdleController.setAlarmsActive(true);
+ mDeviceIdleController.setActiveIdleOpsForTest(1);
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
+
+ // Even with active ops, PRE_IDLE should go to IDLE.
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ // Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+ mDeviceIdleController.stepLightIdleStateLocked("testing");
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
public void testExitMaintenanceEarlyIfNeededLocked_deep_noActiveOps() {
mDeviceIdleController.setJobsActive(false);
mDeviceIdleController.setAlarmsActive(false);
@@ -903,6 +1170,7 @@
mDeviceIdleController.becomeActiveLocked("testing", 0);
break;
case STATE_LOCATING:
+ mInjector.locationManager = mLocationManager;
doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(
anyString());
// Fallthrough to step loop.
@@ -917,7 +1185,6 @@
setScreenOn(false);
setChargingOn(false);
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
- //fail(stateToString(mDeviceIdleController.getState()));
int count = 0;
while (mDeviceIdleController.getState() != state) {
// Stepping through each state ensures that the proper features are turned
@@ -925,7 +1192,8 @@
mDeviceIdleController.stepIdleStateLocked("testing");
count++;
if (count > 10) {
- fail(stateToString(mDeviceIdleController.getState()));
+ fail("Infinite loop. Check test configuration. Currently at " +
+ stateToString(mDeviceIdleController.getState()));
}
}
break;
@@ -954,7 +1222,8 @@
count++;
if (count > 10) {
- fail(lightStateToString(mDeviceIdleController.getLightState()));
+ fail("Infinite loop. Check test configuration. Currently at " +
+ lightStateToString(mDeviceIdleController.getLightState()));
}
}
break;
@@ -979,6 +1248,14 @@
mDeviceIdleController.updateInteractivityLocked();
}
+ private void setNetworkConnected(boolean connected) {
+ mInjector.connectivityService = mConnectivityService;
+ final NetworkInfo ani = mock(NetworkInfo.class);
+ doReturn(connected).when(ani).isConnected();
+ doReturn(ani).when(mConnectivityService).getActiveNetworkInfo();
+ mDeviceIdleController.updateConnectivityState(null);
+ }
+
private void verifyStateConditions(int expectedState) {
int curState = mDeviceIdleController.getState();
assertEquals(
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
rename to services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1752479..b63138e 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -16,19 +16,27 @@
package com.android.server.job.controllers;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.junit.Assert.assertEquals;
import android.app.job.JobInfo;
import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
import android.os.SystemClock;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
import java.time.Clock;
import java.time.ZoneOffset;
@@ -37,8 +45,17 @@
public class JobStatusTest {
private static final double DELTA = 0.00001;
+ private MockitoSession mMockingSession;
+
@Before
public void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ doReturn(mock(PackageManagerInternal.class))
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
@@ -48,6 +65,13 @@
Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
}
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
@Test
public void testFraction() throws Exception {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 2957267..878179b 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -24,7 +24,8 @@
services.net \
services.usage \
guava \
- androidx-test \
+ androidx.test.runner \
+ androidx.test.rules \
mockito-target-minus-junit4 \
platform-test-annotations \
ShortcutManagerTestUtils \
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 1eb88ba..113ee2d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -26,13 +26,21 @@
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
+import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.Process.SYSTEM_UID;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -124,6 +132,7 @@
import android.text.format.Time;
import android.util.DataUnit;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -171,6 +180,7 @@
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
@@ -1644,6 +1654,76 @@
true);
}
+ /**
+ * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * conditions.
+ */
+ @Test
+ public void testIsUidNetworkingBlocked() {
+ final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
+
+ // Metered network. Data saver on.
+ expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+
+ // Metered network. Data saver off.
+ expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+
+ // Non-metered network. Data saver on.
+ expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+
+ // Non-metered network. Data saver off. The result is the same as previous case since
+ // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
+ verifyNetworkBlockedState(
+ false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+ }
+
+ private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
+ ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
+ final NetworkPolicyManagerInternal npmi = LocalServices
+ .getService(NetworkPolicyManagerInternal.class);
+
+ for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
+ final boolean expectedResult = pair.first;
+ final int rule = pair.second;
+ assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
+ expectedResult,
+ npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
+ npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ }
+ }
+
+ private String formatBlockedStateError(int uid, int rule, boolean metered,
+ boolean backgroundRestricted) {
+ return String.format(
+ "Unexpected BlockedState: (uid=%d, rule=%s, metered=%b, backgroundRestricted=%b)",
+ uid, uidRulesToString(rule), metered, backgroundRestricted);
+ }
+
private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
return SubscriptionPlan.Builder
.createRecurringMonthly(start)
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index 43438b9..8022532 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -124,7 +124,7 @@
"/storage/emulated/0/Android/sandbox/com.grey/foo.jpg",
"/storage/emulated/0/foo.jpg", PKG_GREY);
assertTranslation(
- "/storage/emulated/0/Android/sandbox/shared/colors/foo.jpg",
+ "/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
"/storage/emulated/0/foo.jpg", PKG_RED);
}
@@ -134,7 +134,7 @@
"/storage/0000-0000/Android/sandbox/com.grey/foo/bar.jpg",
"/storage/0000-0000/foo/bar.jpg", PKG_GREY);
assertTranslation(
- "/storage/0000-0000/Android/sandbox/shared/colors/foo/bar.jpg",
+ "/storage/0000-0000/Android/sandbox/shared:colors/foo/bar.jpg",
"/storage/0000-0000/foo/bar.jpg", PKG_RED);
}
@@ -147,7 +147,7 @@
// Accessing other package paths goes into sandbox
assertTranslation(
- "/storage/emulated/0/Android/sandbox/shared/colors/"
+ "/storage/emulated/0/Android/sandbox/shared:colors/"
+ "Android/data/com.grey/foo.jpg",
"/storage/emulated/0/Android/data/com.grey/foo.jpg", PKG_RED);
}
@@ -192,7 +192,7 @@
// Sandboxes can't see paths in other sandboxes
try {
mService.translateSystemToApp(
- "/storage/emulated/0/Android/sandbox/shared/colors/foo.jpg",
+ "/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
PKG_GREY, UserHandle.USER_SYSTEM);
fail();
} catch (SecurityException expected) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index c88b873..feffeef 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -86,12 +86,8 @@
final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
final MessageCapturingHandler mMessageCapturingHandler =
- new MessageCapturingHandler(new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- return mMagnificationController.handleMessage(msg);
- }
- });
+ new MessageCapturingHandler(null);
+
final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
MagnificationController.SettingsBridge mMockSettingsBridge;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 79e4d70..032074a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -29,9 +29,11 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Message;
@@ -44,7 +46,9 @@
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
+import com.android.server.wm.WindowManagerInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -101,8 +105,15 @@
public static final float DEFAULT_Y = 299;
private Context mContext;
- private AccessibilityManagerService mAms;
- private MagnificationController mMagnificationController;
+ final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
+ final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
+ final MessageCapturingHandler mMessageCapturingHandler =
+ new MessageCapturingHandler(null);
+ final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
+ MagnificationController.SettingsBridge mMockSettingsBridge =
+ mock(MagnificationController.SettingsBridge.class);
+ MagnificationController mMagnificationController;
+
private OffsettableClock mClock;
private MagnificationGestureHandler mMgh;
private TestHandler mHandler;
@@ -112,9 +123,9 @@
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
- mAms = new AccessibilityManagerService(mContext);
- mMagnificationController = new MagnificationController(
- mContext, mAms, /* lock */ new Object()) {
+ mMagnificationController = new MagnificationController(mContext, mMockAms, new Object(),
+ mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator,
+ mMockSettingsBridge) {
@Override
public boolean magnificationRegionContains(float x, float y) {
return true;
@@ -123,7 +134,7 @@
@Override
void setForceShowMagnifiableBounds(boolean show) {}
};
- mMagnificationController.mRegistered = true;
+ mMagnificationController.register();
mClock = new OffsettableClock.Stopped();
boolean detectTripleTap = true;
@@ -131,6 +142,11 @@
mMgh = newInstance(detectTripleTap, detectShortcutTrigger);
}
+ @After
+ public void tearDown() {
+ mMagnificationController.unregister();
+ }
+
@NonNull
private MagnificationGestureHandler newInstance(boolean detectTripleTap,
boolean detectShortcutTrigger) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
index ea90ffd..0da5742 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -20,11 +20,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
import android.platform.test.annotations.Presubmit;
@@ -131,4 +134,57 @@
new ActivityBuilder(mService).setTask(fullscreenTask).build();
return fullscreenStack;
}
+
+ /**
+ * Verifies the correct activity is returned when querying the top running activity.
+ */
+ @Test
+ public void testTopRunningActivity() {
+ // Create stack to hold focus.
+ final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ final KeyguardController keyguard = mSupervisor.getKeyguardController();
+ final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ // Make sure the top running activity is not affected when keyguard is not locked.
+ assertTopRunningActivity(activity, display);
+
+ // Check to make sure activity not reported when it cannot show on lock and lock is on.
+ doReturn(true).when(keyguard).isKeyguardLocked();
+ assertEquals(activity, display.topRunningActivity());
+ assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+ // Change focus to stack with activity.
+ stack.moveToFront("focusChangeToTestStack");
+ assertEquals(stack, display.getFocusedStack());
+ assertEquals(activity, display.topRunningActivity());
+ assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+ // Add activity that should be shown on the keyguard.
+ final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(stack)
+ .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+ .build();
+
+ // Ensure the show when locked activity is returned.
+ assertTopRunningActivity(showWhenLockedActivity, display);
+
+ // Change focus back to empty stack.
+ emptyStack.moveToFront("focusChangeToEmptyStack");
+ assertEquals(emptyStack, display.getFocusedStack());
+ // If there is no running activity in focused stack, the running activity in next focusable
+ // stack should be returned.
+ assertTopRunningActivity(showWhenLockedActivity, display);
+ }
+
+ private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
+ assertEquals(top, display.topRunningActivity());
+ assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index c44e492..f42f5b1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -64,6 +64,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -102,6 +103,7 @@
* com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
*/
@SmallTest
+@FlakyTest(bugId = 113616538)
@RunWith(AndroidJUnit4.class)
public class ActivityManagerServiceTest {
private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
@@ -173,7 +175,7 @@
true); // expectNotify
// Explicitly setting the seq counter for more verification.
- mAms.mProcStateSeqCounter = 42;
+ mAms.mProcessList.mProcStateSeqCounter = 42;
// Uid state is not moving from background to foreground or vice versa.
verifySeqCounterAndInteractions(uidRec,
@@ -260,7 +262,7 @@
final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid, null);
appRec.thread = Mockito.mock(IApplicationThread.class);
- mAms.mLruProcesses.add(appRec);
+ mAms.mProcessList.mLruProcesses.add(appRec);
return uidRec;
}
@@ -275,11 +277,11 @@
uidRec.curProcState = curState;
mAms.incrementProcStateSeqAndNotifyAppsLocked();
- assertEquals(expectedGlobalCounter, mAms.mProcStateSeqCounter);
+ assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
- for (int i = mAms.mLruProcesses.size() - 1; i >= 0; --i) {
- final ProcessRecord app = mAms.mLruProcesses.get(i);
+ for (int i = mAms.mProcessList.getLruSizeLocked() - 1; i >= 0; --i) {
+ final ProcessRecord app = mAms.mProcessList.mLruProcesses.get(i);
// AMS should notify apps only for block states other than NETWORK_STATE_NO_CHANGE.
if (app.uid == uidRec.uid && expectedBlockState == NETWORK_STATE_BLOCK) {
verify(app.thread).setNetworkBlockSeq(uidRec.curProcStateSeq);
@@ -829,4 +831,4 @@
mRestricted = restricted;
}
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index ba25b16..2dfb375 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -16,38 +16,59 @@
package com.android.server.am;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
import android.app.IActivityManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.RemoteException;
-import android.test.AndroidTestCase;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
import java.util.List;
-public class ActivityManagerTest extends AndroidTestCase {
+import androidx.test.filters.FlakyTest;
- IActivityManager service;
- @Override
+/**
+ * Tests for {@link ActivityManager}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.am.ActivityManagerTest
+ */
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class ActivityManagerTest {
+
+ private IActivityManager service;
+
+ @Before
public void setUp() throws Exception {
- super.setUp();
service = ActivityManager.getService();
}
+ @Test
public void testTaskIdsForRunningUsers() throws RemoteException {
- for(int userId : service.getRunningUserIds()) {
+ int[] runningUserIds = service.getRunningUserIds();
+ assertThat(runningUserIds).isNotEmpty();
+ for (int userId : runningUserIds) {
testTaskIdsForUser(userId);
}
}
private void testTaskIdsForUser(int userId) throws RemoteException {
- List<ActivityManager.RecentTaskInfo> recentTasks = service.getRecentTasks(
- 100, 0, userId).getList();
- if(recentTasks != null) {
- for(ActivityManager.RecentTaskInfo recentTask : recentTasks) {
- int taskId = recentTask.persistentId;
+ List<?> recentTasks = service.getRecentTasks(100, 0, userId).getList();
+ if (recentTasks != null) {
+ for (Object elem : recentTasks) {
+ assertThat(elem).isInstanceOf(RecentTaskInfo.class);
+ RecentTaskInfo recentTask = (RecentTaskInfo) elem;
+ int taskId = recentTask.taskId;
assertEquals("The task id " + taskId + " should not belong to user " + userId,
- taskId / UserHandle.PER_USER_RANGE, userId);
+ taskId / UserHandle.PER_USER_RANGE, userId);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 81a0934..2c993d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -18,13 +18,13 @@
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
@@ -34,9 +34,11 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -304,62 +306,6 @@
}
/**
- * Verifies the correct activity is returned when querying the top running activity.
- */
- @Test
- public void testTopRunningActivity() throws Exception {
- // Create stack to hold focus
- final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
- final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
-
- // Make sure the top running activity is not affected when keyguard is not locked
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Check to make sure activity not reported when it cannot show on lock and lock is on.
- doReturn(true).when(keyguard).isKeyguardLocked();
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Change focus to stack with activity.
- stack.moveToFront("focusChangeToTestStack");
- assertEquals(stack, display.getFocusedStack());
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Add activity that should be shown on the keyguard.
- final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .setStack(stack)
- .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
- .build();
-
- // Ensure the show when locked activity is returned.
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Change focus back to empty stack
- emptyStack.moveToFront("focusChangeToEmptyStack");
- assertEquals(emptyStack, display.getFocusedStack());
- // Looking for running activity only in top and focused stack, so nothing should be returned
- // from empty stack.
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
- }
-
- /**
* Verify that split-screen primary stack will be chosen if activity is launched that targets
* split-screen secondary, but a matching existing instance is found on top of split-screen
* primary stack.
@@ -407,6 +353,57 @@
}
/**
+ * Verify that home stack would be moved to front when the top activity is Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnTop() throws Exception {
+ // Create stack/task on default display.
+ final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+ // Create Recents on top of the display.
+ final ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ new ActivityBuilder(mService).setTask(task).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(display).moveHomeStackToFront(contains(reason));
+ }
+
+ /**
+ * Verify that home stack won't be moved to front if the top activity on other display is
+ * Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() throws Exception {
+ // Create stack/task on default display.
+ final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+ // Create Recents on secondary display.
+ final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+ ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ new ActivityBuilder(mService).setTask(task).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(display, never()).moveHomeStackToFront(contains(reason));
+ }
+
+ /**
* Verify if a stack is not at the topmost position, it should be able to resume its activity if
* the stack is the top focused.
*/
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 5fcd2aa..53f67af 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -26,6 +26,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
@@ -35,10 +38,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
@@ -655,6 +662,39 @@
}
@Test
+ public void testFinishCurrentActivity() {
+ // Create 2 activities on a new display.
+ final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // There is still an activity1 in stack1 so the activity2 should be added to finishing list
+ // that will be destroyed until idle.
+ final ActivityRecord activity2 = finishCurrentActivity(stack2);
+ assertEquals(FINISHING, activity2.getState());
+ assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
+
+ // The display becomes empty. Since there is no next activity to be idle, the activity
+ // should be destroyed immediately with updating configuration to restore original state.
+ final ActivityRecord activity1 = finishCurrentActivity(stack1);
+ assertEquals(DESTROYING, activity1.getState());
+ verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+ eq(display.mDisplayId), anyBoolean(), anyBoolean());
+ }
+
+ private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+ final ActivityRecord activity = stack.topRunningActivityLocked();
+ assertNotNull(activity);
+ activity.setState(PAUSED, "finishCurrentActivity");
+ activity.makeFinishingLocked();
+ stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE,
+ false /* oomAdj */, "finishCurrentActivity");
+ return activity;
+ }
+
+ @Test
public void testShouldSleepActivities() throws Exception {
// When focused activity and keyguard is going away, we should not sleep regardless
// of the display state
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 86541b9..65e4fa0 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -35,6 +35,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
@@ -165,17 +166,20 @@
public void testSuspendedPackage() {
mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
final String suspendingPackage = "com.test.suspending.package";
- final String dialogMessage = "Test Message";
+ final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+ .setMessage("Test Message")
+ .setIcon(0x11110001)
+ .build();
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(suspendingPackage);
- when(mPackageManagerInternal.getSuspendedDialogMessage(TEST_PACKAGE_NAME, TEST_USER_ID))
- .thenReturn(dialogMessage);
+ when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(dialogInfo);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
// Check intent parameters
- assertEquals(dialogMessage,
- mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE));
+ assertEquals(dialogInfo,
+ mInterceptor.mIntent.getParcelableExtra(SuspendedAppActivity.EXTRA_DIALOG_INFO));
assertEquals(suspendingPackage,
mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE));
assertEquals(TEST_PACKAGE_NAME,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index bac4a52..ba64b51 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -37,7 +37,8 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityDisplay.POSITION_TOP;
+import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -48,6 +49,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -206,11 +208,11 @@
prepareStarter(launchFlags);
final IApplicationThread caller = mock(IApplicationThread.class);
- // If no caller app, return {@code null} {@link ProcessRecord}.
- final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new ProcessRecord(service.mAm, mock(ApplicationInfo.class), null, 0, null);
-
- doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
+ final WindowProcessController wpc =
+ containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
+ ? null : new WindowProcessController(
+ service, mock(ApplicationInfo.class),null, 0, -1, null, null, null);
+ doReturn(wpc).when(service).getProcessController(anyObject());
final Intent intent = new Intent();
intent.setFlags(launchFlags);
@@ -354,10 +356,12 @@
invocation -> {
throw new RuntimeException("Not stubbed");
});
- doReturn(mockPackageManager).when(mService.mAm).getPackageManagerInternalLocked();
+ doReturn(mockPackageManager).when(mService).getPackageManagerInternalLocked();
// Never review permissions
doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
+ doNothing().when(mockPackageManager).grantEphemeralAccess(
+ anyInt(), any(), anyInt(), anyInt());
final Intent intent = new Intent();
intent.addFlags(launchFlags);
@@ -408,8 +412,9 @@
.setActivityOptions(new SafeActivityOptions(options))
.execute();
- // verify that values are passed to the modifier.
- verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
+ // verify that values are passed to the modifier. Values are passed twice -- once for
+ // setting initial state, another when task is created.
+ verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
any(), any());
}
@@ -510,7 +515,7 @@
*/
@Test
public void testActivityStartsLogging_noLoggingWhenDisabled() {
- doReturn(false).when(mService.mAm).isActivityStartsLoggingEnabled();
+ doReturn(false).when(mService).isActivityStartsLoggingEnabled();
doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
@@ -528,7 +533,7 @@
@Test
public void testActivityStartsLogging_logsWhenEnabled() {
// note: conveniently this package doesn't have any activity visible
- doReturn(true).when(mService.mAm).isActivityStartsLoggingEnabled();
+ doReturn(true).when(mService).isActivityStartsLoggingEnabled();
doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
@@ -558,23 +563,13 @@
false /* mockGetLaunchStack */);
// Create a secondary display at bottom.
- final TestActivityDisplay secondaryDisplay = spy(addNewActivityDisplayAt(POSITION_BOTTOM));
+ final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+ mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Create an activity record on the top of secondary display.
- final ComponentName componentName = ComponentName.createRelative(
- DEFAULT_COMPONENT_PACKAGE_NAME,
- DEFAULT_COMPONENT_PACKAGE_NAME + ".ReusableActivity");
- final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
- .setComponent(componentName)
- .setStack(stack)
- .build();
- final ActivityRecord topActivityOnSecondaryDisplay = new ActivityBuilder(mService)
- .setComponent(componentName)
- .setLaunchMode(LAUNCH_SINGLE_TASK)
- .setTask(taskRecord)
- .build();
+ final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
// Put an activity on default display as the top focused activity.
new ActivityBuilder(mService).setCreateTask(true).build();
@@ -596,6 +591,59 @@
}
/**
+ * This test ensures that when starting an existing non-top single task activity on secondary
+ * display which is the top focused display, it should bring the task to front without creating
+ * unused stack.
+ */
+ @Test
+ public void testBringTaskToFrontOnSecondaryDisplay() {
+ final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+ false /* mockGetLaunchStack */);
+
+ // Create a secondary display with an activity.
+ final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+ mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+ final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
+ secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+
+ // Create another activity on top of the secondary display.
+ final ActivityStack topStack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TaskRecord topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
+ new ActivityBuilder(mService).setTask(topTask).build();
+
+ // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
+ .setIntent(singleTaskActivity.intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Ensure result is moving existing task to front.
+ assertEquals(START_TASK_TO_FRONT, result);
+
+ // Ensure secondary display only creates two stacks.
+ verify(secondaryDisplay, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
+ }
+
+ private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
+ final ComponentName componentName = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME,
+ DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
+ final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
+ .setComponent(componentName)
+ .setStack(stack)
+ .build();
+ return new ActivityBuilder(mService)
+ .setComponent(componentName)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setTask(taskRecord)
+ .build();
+ }
+
+ /**
* This test ensures that a reused top activity in the top focused stack is able to be
* reparented to another display.
*/
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 20088619..01d51e4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -58,8 +58,9 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.HandlerThread;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
@@ -69,7 +70,9 @@
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
+import com.android.server.ServiceThread;
import com.android.server.wm.AppWindowContainerController;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.RootWindowContainerController;
@@ -82,6 +85,7 @@
import org.junit.Before;
import org.mockito.MockitoAnnotations;
+import java.io.File;
import java.util.List;
/**
@@ -97,7 +101,7 @@
new DexmakerShareClassLoaderRule();
private final Context mContext = InstrumentationRegistry.getContext();
- private HandlerThread mHandlerThread;
+ final TestInjector mTestInjector = new TestInjector();
ActivityTaskManagerService mService;
ActivityStackSupervisor mSupervisor;
@@ -115,13 +119,12 @@
MockitoAnnotations.initMocks(this);
AttributeCache.init(mContext);
}
- mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
- mHandlerThread.start();
+ mTestInjector.setUp();
}
@After
public void tearDown() {
- mHandlerThread.quitSafely();
+ mTestInjector.tearDown();
}
protected ActivityTaskManagerService createActivityTaskManagerService() {
@@ -143,7 +146,7 @@
}
ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
- final TestActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+ final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
setupActivityManagerService(am, atm);
return am;
}
@@ -162,7 +165,8 @@
void setupActivityManagerService(
TestActivityManagerService am, TestActivityTaskManagerService atm) {
- atm.setActivityManagerService(am);
+ atm.setActivityManagerService(am, am.mHandlerThread.getLooper(), am.mIntentFirewall,
+ am.mPendingIntentController);
atm.mAmInternal = am.getLocalService();
am.mAtmInternal = atm.getLocalService();
// Makes sure the supervisor is using with the spy object.
@@ -172,6 +176,7 @@
doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+ am.mActivityTaskManager = atm;
am.mWindowManager = prepareMockWindowManager();
atm.setWindowManager(am.mWindowManager);
@@ -191,8 +196,6 @@
// An id appended to the end of the component name to make it unique
private static int sCurrentActivityId = 0;
-
-
private final ActivityTaskManagerService mService;
private ComponentName mComponent;
@@ -486,6 +489,40 @@
}
}
+ private static class TestInjector extends ActivityManagerService.Injector {
+ private ServiceThread mHandlerThread;
+
+ @Override
+ public Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return null;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public boolean isNetworkRestrictedForUid(int uid) {
+ return false;
+ }
+
+ void setUp() {
+ mHandlerThread = new ServiceThread("ActivityTestsThread",
+ Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+ mHandlerThread.start();
+ }
+
+ void tearDown() {
+ mHandlerThread.quitSafely();
+ }
+ }
+
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
@@ -494,8 +531,8 @@
private ActivityManagerInternal mInternal;
- TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
- super(context, atm);
+ TestActivityManagerService(TestInjector testInjector) {
+ super(testInjector, testInjector.mHandlerThread);
mUgmInternal = mock(UriGrantsManagerInternal.class);
}
@@ -545,6 +582,11 @@
ActivityDisplay getDefaultDisplay() {
return mDisplay;
}
+
+ @Override
+ void setWindowManager(WindowManagerService wm) {
+ mWindowManager = wm;
+ }
}
protected static class TestActivityDisplay extends ActivityDisplay {
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 3819e21..06d41f1 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -21,6 +21,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,6 +38,7 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
+@FlakyTest(bugId = 113616538)
public class AppErrorDialogTest {
private Context mContext;
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index a030210..1b823ff 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
import android.view.IWindowManager;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -67,6 +68,7 @@
* runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
*/
@MediumTest
+@FlakyTest(bugId = 113616538)
@RunWith(AndroidJUnit4.class)
public class AssistDataRequesterTest extends ActivityTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 62c5734..75f7c4c 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -45,7 +45,7 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class BroadcastRecordTest extends ActivityTestsBase {
+public class BroadcastRecordTest {
@Test
public void testCleanupDisabledPackageReceivers() {
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index fe8256e..719e0ed 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -132,15 +132,36 @@
settingsBundle.containsKey(TEST_SETTING_SYSTEM_STRING));
}
+ @Test
+ public void testPopulateSettings_settingDeleted() {
+ Settings.Secure.putInt(mContentResolver, TEST_SETTING_SECURE_INT, TEST_INT);
+ Settings.Global.putFloat(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, TEST_FLOAT);
+ Settings.System.putString(mContentResolver, TEST_SETTING_SYSTEM_STRING, TEST_STRING);
+
+ Bundle settingsBundle = getPopulatedBundle();
+
+ assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
+ TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
+ assertEquals("Unexpected value of " + TEST_SETTING_GLOBAL_FLOAT,
+ TEST_FLOAT, settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT), 0);
+ assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
+ TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
+
+ Settings.Global.putString(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, null);
+ settingsBundle = getPopulatedBundle();
+
+ assertFalse("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT,
+ settingsBundle.containsKey(TEST_SETTING_GLOBAL_FLOAT));
+ assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
+ TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
+ assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
+ TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
+
+ }
+
private Bundle getPopulatedBundle() {
- final Bundle settingsBundle = new Bundle();
- mCoreSettingsObserver.populateSettings(settingsBundle,
- CoreSettingsObserver.sGlobalSettingToTypeMap);
- mCoreSettingsObserver.populateSettings(settingsBundle,
- CoreSettingsObserver.sSecureSettingToTypeMap);
- mCoreSettingsObserver.populateSettings(settingsBundle,
- CoreSettingsObserver.sSystemSettingToTypeMap);
- return settingsBundle;
+ mCoreSettingsObserver.onChange(false);
+ return mCoreSettingsObserver.getCoreSettingsLocked();
}
private class TestInjector extends Injector {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 1276f65..27e8c63 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -116,8 +116,7 @@
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
mService = spy(new MyTestActivityTaskManagerService(mContext));
- final TestActivityManagerService am =
- spy(new MyTestActivityManagerService(mContext, mService));
+ final TestActivityManagerService am = spy(new MyTestActivityManagerService());
setupActivityManagerService(am, mService);
mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
@@ -848,8 +847,8 @@
}
private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
- super(context, atm);
+ MyTestActivityManagerService() {
+ super(mTestInjector);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 0d1302f..169204f 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -29,6 +29,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -97,6 +98,13 @@
mResult.reset();
}
+ @Test
+ public void testReturnsSkipWithEmptyActivity() {
+ final TaskRecord task = new TaskBuilder(mSupervisor).build();
+ assertEquals(RESULT_SKIP, mTarget.onCalculate(task, /* layout */ null,
+ /* activity */ null, /* source */ null, /* options */ null, mCurrent, mResult));
+ }
+
// =============================
// Display ID Related Tests
// =============================
@@ -189,7 +197,7 @@
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+ assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
WINDOWING_MODE_FULLSCREEN);
}
@@ -277,7 +285,7 @@
}
@Test
- public void testNonEmptyLayoutInfersFreeformWithResizeableActivity() {
+ public void testNonEmptyLayoutUsesFullscreenWithResizeableActivity() {
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setWidth(120).setHeight(80).build();
@@ -286,7 +294,7 @@
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+ assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
WINDOWING_MODE_FULLSCREEN);
}
@@ -713,21 +721,6 @@
}
@Test
- public void testNonEmptyLayoutBoundsWithResizeableActivity() {
- final ActivityDisplay display = mSupervisor.getActivityDisplay(DEFAULT_DISPLAY);
- display.setBounds(new Rect(0, 0, 1920, 1080));
- final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
- .setWidth(120).setHeight(80).build();
-
- mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
-
- assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
- /* source */ null, /* options */ null, mCurrent, mResult));
-
- assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds);
- }
-
- @Test
public void testRespectBoundsFromFullyResolvedCurrentParam_NonEmptyBounds() {
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 8d54bc2..48bfe1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -16,34 +16,43 @@
package com.android.server.am;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
import android.content.pm.UserInfo;
-import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
-import android.test.AndroidTestCase;
-import android.util.Log;
+import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
-import com.android.server.am.TaskPersister;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
-import java.io.File;
-import java.util.Random;
+import androidx.test.filters.FlakyTest;
/**
- * atest FrameworksServicesTests:TaskPersisterTest
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:TaskPersisterTest
*/
-public class TaskPersisterTest extends AndroidTestCase {
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class TaskPersisterTest {
private static final String TEST_USER_NAME = "AM-Test-User";
private TaskPersister mTaskPersister;
private int testUserId;
private UserManager mUserManager;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- mUserManager = UserManager.get(getContext());
- mTaskPersister = new TaskPersister(getContext().getFilesDir());
+ mUserManager = UserManager.get(getTargetContext());
+ mTaskPersister = new TaskPersister(getTargetContext().getFilesDir());
// In ARC, the maximum number of supported users is one, which is different from the ones of
// most phones (more than 4). This prevents TaskPersisterTest from creating another user for
// test. However, since guest users can be added as much as possible, we create guest user
@@ -51,9 +60,8 @@
testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
}
- @Override
+ @After
public void tearDown() throws Exception {
- super.tearDown();
mTaskPersister.unloadUserDataFromMemory(testUserId);
removeUser(testUserId);
}
@@ -64,6 +72,7 @@
return taskId;
}
+ @Test
public void testTaskIdsPersistence() {
SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
for (int i = 0; i < 100; i++) {
@@ -72,21 +81,18 @@
mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
SparseBooleanArray newTaskIdsOnFile = mTaskPersister
.loadPersistedTaskIdsForUser(testUserId);
- assertTrue("TaskIds written differ from TaskIds read back from file",
- taskIdsOnFile.equals(newTaskIdsOnFile));
+ assertEquals("TaskIds written differ from TaskIds read back from file",
+ taskIdsOnFile, newTaskIdsOnFile);
}
private int createUser(String name, int flags) {
UserInfo user = mUserManager.createUser(name, flags);
- if (user == null) {
- fail("Error while creating the test user: " + TEST_USER_NAME);
- }
+ assertNotNull("Error while creating the test user: " + TEST_USER_NAME, user);
return user.id;
}
private void removeUser(int userId) {
- if (!mUserManager.removeUser(userId)) {
- fail("Error while removing the test user: " + TEST_USER_NAME);
- }
+ boolean userRemoved = mUserManager.removeUser(userId);
+ assertTrue("Error while removing the test user: " + TEST_USER_NAME, userRemoved);
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index fa8a09c..27766d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -69,13 +69,11 @@
private static final String TASK_TAG = "task";
- private ActivityManagerService mService;
-
@Before
public void setUp() throws Exception {
super.setUp();
TaskRecord.setTaskRecordFactory(null);
- mService = createActivityManagerService();
+ setupActivityTaskManagerService();
}
@Test
@@ -150,7 +148,7 @@
}
private TaskRecord createTaskRecord(int taskId) {
- return new TaskRecord(mService.mActivityTaskManager, taskId, new Intent(), null, null, null,
+ return new TaskRecord(mService, taskId, new Intent(), null, null, null,
ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index cc4f519..75e1d0d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,41 @@
package com.android.server.am;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+
+import static com.google.android.collect.Lists.newArrayList;
+import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+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.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -31,80 +66,62 @@
import android.os.RemoteException;
import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
-import org.mockito.Mockito;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
-import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-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.times;
-import static org.mockito.Mockito.when;
+import androidx.test.filters.SmallTest;
/**
- * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ * Tests for {@link UserController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.am.UserControllerTest
*/
@Presubmit
-public class UserControllerTest extends AndroidTestCase {
+@SmallTest
+public class UserControllerTest {
private static final int TEST_USER_ID = 10;
private static final int NONEXIST_USER_ID = 2;
private static String TAG = UserControllerTest.class.getSimpleName();
private UserController mUserController;
private TestInjector mInjector;
- private static final List<String> START_FOREGROUND_USER_ACTIONS =
- Arrays.asList(
- Intent.ACTION_USER_STARTED,
- Intent.ACTION_USER_SWITCHED,
- Intent.ACTION_USER_STARTING);
+ private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
+ Intent.ACTION_USER_STARTED,
+ Intent.ACTION_USER_SWITCHED,
+ Intent.ACTION_USER_STARTING);
- private static final List<String> START_BACKGROUND_USER_ACTIONS =
- Arrays.asList(
- Intent.ACTION_USER_STARTED,
- Intent.ACTION_LOCKED_BOOT_COMPLETED,
- Intent.ACTION_USER_STARTING);
+ private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
+ Intent.ACTION_USER_STARTED,
+ Intent.ACTION_LOCKED_BOOT_COMPLETED,
+ Intent.ACTION_USER_STARTING);
- private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES =
- new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
- SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
+ private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
+ REPORT_USER_SWITCH_MSG,
+ USER_SWITCH_TIMEOUT_MSG,
+ SYSTEM_USER_START_MSG,
+ SYSTEM_USER_CURRENT_MSG);
- private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES =
- new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG));
+ private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
+ SYSTEM_USER_START_MSG,
+ REPORT_LOCKED_BOOT_COMPLETE_MSG);
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
runWithDexmakerShareClassLoader(() -> {
- mInjector = Mockito.spy(new TestInjector(getContext()));
+ mInjector = spy(new TestInjector(getTargetContext()));
doNothing().when(mInjector).clearAllLockedTasks(anyString());
doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
@@ -114,58 +131,54 @@
});
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
mInjector.handlerThread.quit();
- Mockito.validateMockitoUsage();
+ validateMockitoUsage();
}
- @SmallTest
- public void testStartUser_foreground() throws RemoteException {
+ @Test
+ public void testStartUser_foreground() {
mUserController.startUser(TEST_USER_ID, true /* foreground */);
- Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
- Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
- Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
- Mockito.verify(mInjector).clearAllLockedTasks(anyString());
+ verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+ verify(mInjector.getWindowManager()).setSwitchingUser(true);
+ verify(mInjector).clearAllLockedTasks(anyString());
startForegroundUserAssertions();
}
- @SmallTest
- public void testStartUser_background() throws RemoteException {
+ @Test
+ public void testStartUser_background() {
mUserController.startUser(TEST_USER_ID, false /* foreground */);
- Mockito.verify(
- mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
- Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector, never()).clearAllLockedTasks(anyString());
+ verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ verify(mInjector, never()).clearAllLockedTasks(anyString());
startBackgroundUserAssertions();
}
- @SmallTest
- public void testStartUserUIDisabled() throws RemoteException {
+ @Test
+ public void testStartUserUIDisabled() {
mUserController.mUserSwitchUiEnabled = false;
mUserController.startUser(TEST_USER_ID, true /* foreground */);
- Mockito.verify(mInjector.getWindowManager(), never())
- .startFreezingScreen(anyInt(), anyInt());
- Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
- Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
startForegroundUserAssertions();
}
private void startUserAssertions(
- List<String> expectedActions, Set<Integer> expectedMessageCodes)
- throws RemoteException {
+ List<String> expectedActions, Set<Integer> expectedMessageCodes) {
assertEquals(expectedActions, getActions(mInjector.sentIntents));
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
}
- private void startBackgroundUserAssertions() throws RemoteException {
+ private void startBackgroundUserAssertions() {
startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
}
- private void startForegroundUserAssertions() throws RemoteException {
+ private void startForegroundUserAssertions() {
startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
@@ -177,15 +190,15 @@
assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2);
}
- @SmallTest
- public void testFailedStartUserInForeground() throws RemoteException {
+ @Test
+ public void testFailedStartUserInForeground() {
mUserController.mUserSwitchUiEnabled = false;
mUserController.startUserInForeground(NONEXIST_USER_ID);
- Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(false);
+ verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+ verify(mInjector.getWindowManager()).setSwitchingUser(false);
}
- @SmallTest
+ @Test
public void testDispatchUserSwitch() throws RemoteException {
// Prepare mock observer and register it
IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -206,7 +219,7 @@
// Call dispatchUserSwitch and verify that observer was called only once
mInjector.handler.clearAllRecordedMessages();
mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+ verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG);
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -220,7 +233,7 @@
assertEquals("Unexpected new user id", TEST_USER_ID, conMsg.arg2);
}
- @SmallTest
+ @Test
public void testDispatchUserSwitchBadReceiver() throws RemoteException {
// Prepare mock observer which doesn't notify the callback and register it
IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -236,14 +249,14 @@
// Call dispatchUserSwitch and verify that observer was called only once
mInjector.handler.clearAllRecordedMessages();
mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+ verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
// Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout)
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
- assertTrue("No messages should be sent", actualCodes.isEmpty());
+ assertWithMessage("No messages should be sent").that(actualCodes).isEmpty();
}
- @SmallTest
- public void testContinueUserSwitch() throws RemoteException {
+ @Test
+ public void testContinueUserSwitch() {
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, true);
Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -254,12 +267,12 @@
mInjector.handler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
mUserController.continueUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
continueUserSwitchAssertions();
}
- @SmallTest
- public void testContinueUserSwitchUIDisabled() throws RemoteException {
+ @Test
+ public void testContinueUserSwitchUIDisabled() {
mUserController.mUserSwitchUiEnabled = false;
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, true);
@@ -271,11 +284,11 @@
mInjector.handler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
mUserController.continueUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
continueUserSwitchAssertions();
}
- private void continueUserSwitchAssertions() throws RemoteException {
+ private void continueUserSwitchAssertions() {
Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG);
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -284,7 +297,7 @@
assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1);
}
- @SmallTest
+ @Test
public void testDispatchUserSwitchComplete() throws RemoteException {
// Prepare mock observer and register it
IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -298,12 +311,12 @@
mInjector.handler.clearAllRecordedMessages();
// Mockito can't reset only interactions, so just verify that this hasn't been
// called with 'false' until after dispatchUserSwitchComplete.
- Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
// Call dispatchUserSwitchComplete
mUserController.dispatchUserSwitchComplete(newUserId);
- Mockito.verify(observer, times(1)).onUserSwitchComplete(anyInt());
- Mockito.verify(observer).onUserSwitchComplete(TEST_USER_ID);
- Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+ verify(observer, times(1)).onUserSwitchComplete(anyInt());
+ verify(observer).onUserSwitchComplete(TEST_USER_ID);
+ verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
}
private void setUpUser(int userId, int flags) {
@@ -320,7 +333,7 @@
}
// Should be public to allow mocking
- public static class TestInjector extends UserController.Injector {
+ private static class TestInjector extends UserController.Injector {
TestHandler handler;
TestHandler uiHandler;
HandlerThread handlerThread;
@@ -438,4 +451,4 @@
return super.sendMessageAtTime(msg, uptimeMillis);
}
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 79eba68..92211ec 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -887,7 +887,7 @@
}
@Override
- protected BackupManagerServiceInterface createBackupManagerService() {
+ protected BackupManagerService createBackupManagerService() {
mCreateServiceCallsCount++;
return sBackupManagerServiceMock;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index c3c0788..517b5ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -37,6 +37,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.os.BaseBundle;
import android.os.PersistableBundle;
@@ -200,13 +201,21 @@
PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
final PersistableBundle launcherExtras1 = getPersistableBundle(
PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
- ps1.setSuspended(true, "suspendingPackage1", "dialogMsg1", appExtras1, launcherExtras1, 0);
+
+ final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
+ .setIcon(0x11220001)
+ .setTitle(0x11220002)
+ .setMessage("1st message")
+ .setNeutralButtonText(0x11220003)
+ .build();
+
+ ps1.setSuspended(true, "suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
- ps2.setSuspended(true, "suspendingPackage2", "dialogMsg2", null, null, 0);
+ ps2.setSuspended(true, "suspendingPackage2", null, null, null, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
- ps3.setSuspended(false, "irrelevant", "irrevelant2", null, null, 0);
+ ps3.setSuspended(false, "irrelevant", dialogInfo1, null, null, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
settingsUnderTest.writePackageRestrictionsLPr(0);
@@ -221,7 +230,7 @@
readUserState(0);
assertThat(readPus1.suspended, is(true));
assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
- assertThat(readPus1.dialogMessage, equalTo("dialogMsg1"));
+ assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
is(true));
@@ -230,7 +239,7 @@
readUserState(0);
assertThat(readPus2.suspended, is(true));
assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
- assertThat(readPus2.dialogMessage, equalTo("dialogMsg2"));
+ assertThat(readPus2.dialogInfo, is(nullValue()));
assertThat(readPus2.suspendedAppExtras, is(nullValue()));
assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
@@ -238,7 +247,7 @@
readUserState(0);
assertThat(readPus3.suspended, is(false));
assertThat(readPus3.suspendingPackage, is(nullValue()));
- assertThat(readPus3.dialogMessage, is(nullValue()));
+ assertThat(readPus3.dialogInfo, is(nullValue()));
assertThat(readPus3.suspendedAppExtras, is(nullValue()));
assertThat(readPus3.suspendedLauncherExtras, is(nullValue()));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 4a33ca3..f0ed612 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertThat;
import android.content.pm.PackageUserState;
+import android.content.pm.SuspendDialogInfo;
import android.os.PersistableBundle;
import android.util.ArraySet;
@@ -37,7 +38,7 @@
public class PackageUserStateTest {
@Test
- public void testPackageUserState01() {
+ public void testPackageUserState01() {
final PackageUserState testUserState = new PackageUserState();
PackageUserState oldUserState;
@@ -84,7 +85,7 @@
}
@Test
- public void testPackageUserState02() {
+ public void testPackageUserState02() {
final PackageUserState testUserState01 = new PackageUserState();
PackageUserState oldUserState;
@@ -102,7 +103,7 @@
}
@Test
- public void testPackageUserState03() {
+ public void testPackageUserState03() {
final PackageUserState oldUserState = new PackageUserState();
// only new user state has array defined; different
@@ -138,7 +139,7 @@
}
@Test
- public void testPackageUserState04() {
+ public void testPackageUserState04() {
final PackageUserState oldUserState = new PackageUserState();
// only new user state has array defined; different
@@ -185,15 +186,19 @@
launcherExtras2.putString("name", "launcherExtras2");
final String suspendingPackage1 = "package1";
final String suspendingPackage2 = "package2";
- final String dialogMessage1 = "dialogMessage1";
- final String dialogMessage2 = "dialogMessage2";
+ final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
+ .setMessage("dialogMessage1")
+ .build();
+ final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder()
+ .setMessage("dialogMessage2")
+ .build();
final PackageUserState testUserState1 = new PackageUserState();
testUserState1.suspended = true;
testUserState1.suspendedAppExtras = appExtras1;
testUserState1.suspendedLauncherExtras = launcherExtras1;
testUserState1.suspendingPackage = suspendingPackage1;
- testUserState1.dialogMessage = dialogMessage1;
+ testUserState1.dialogInfo = dialogInfo1;
PackageUserState testUserState2 = new PackageUserState(testUserState1);
assertThat(testUserState1.equals(testUserState2), is(true));
@@ -209,14 +214,14 @@
assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2 = new PackageUserState(testUserState1);
- testUserState2.dialogMessage = dialogMessage2;
+ testUserState2.dialogInfo = dialogInfo2;
assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2 = new PackageUserState(testUserState1);
testUserState2.suspended = testUserState1.suspended = false;
// Everything is different but irrelevant if suspended is false
testUserState2.suspendingPackage = suspendingPackage2;
- testUserState2.dialogMessage = dialogMessage2;
+ testUserState2.dialogInfo = dialogInfo2;
testUserState2.suspendedAppExtras = appExtras2;
testUserState2.suspendedLauncherExtras = launcherExtras2;
assertThat(testUserState1.equals(testUserState2), is(true));
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
new file mode 100644
index 0000000..7eccd67
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.pm.SuspendDialogInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SuspendDialogInfoTest {
+ private static final int VALID_TEST_RES_ID_1 = 0x11110001;
+ private static final int VALID_TEST_RES_ID_2 = 0x11110002;
+
+ private static SuspendDialogInfo.Builder createDefaultDialogBuilder() {
+ return new SuspendDialogInfo.Builder()
+ .setIcon(VALID_TEST_RES_ID_1)
+ .setTitle(VALID_TEST_RES_ID_1)
+ .setMessage(VALID_TEST_RES_ID_1)
+ .setNeutralButtonText(VALID_TEST_RES_ID_1);
+ }
+
+ @Test
+ public void equalsComparesIcons() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+ final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ // Only icon is different
+ dialogBuilder2.setIcon(VALID_TEST_RES_ID_2);
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsComparesTitle() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+ final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ // Only title is different
+ dialogBuilder2.setTitle(VALID_TEST_RES_ID_2);
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsComparesButtonText() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+ final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ // Only button text is different
+ dialogBuilder2.setNeutralButtonText(VALID_TEST_RES_ID_2);
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsComparesMessageIds() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+ final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ // Only message is different
+ dialogBuilder2.setMessage(VALID_TEST_RES_ID_2);
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsIgnoresMessageStringsWhenIdsSet() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+ .setMessage(VALID_TEST_RES_ID_1)
+ .setMessage("1st message");
+ final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+ .setMessage(VALID_TEST_RES_ID_1)
+ .setMessage("2nd message");
+ // String messages different but should get be ignored when resource ids are set
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsComparesMessageStringsWhenNoIdsSet() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+ .setMessage("1st message");
+ final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+ .setMessage("2nd message");
+ // Both have different messages, which are not ignored as resource ids aren't set
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void messageStringClearedWhenResIdSet() {
+ final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+ .setMessage(VALID_TEST_RES_ID_2)
+ .setMessage("Should be cleared on build")
+ .build();
+ assertNull(dialogInfo.getDialogMessage());
+ assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getDialogMessageResId());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index f115b9c..553d234a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -33,6 +33,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.pm.SuspendDialogInfo;
import android.content.res.Resources;
import android.os.BaseBundle;
import android.os.Bundle;
@@ -152,7 +153,7 @@
}
void drainPendingBroadcasts() {
- while (pollForIntent(5) != null);
+ while (pollForIntent(5) != null) ;
}
Intent receiveIntentFromApp() {
@@ -215,15 +216,15 @@
}
private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras,
- String dialogMessage) {
+ SuspendDialogInfo dialogInfo) {
final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
- PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogMessage);
+ PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogInfo);
assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
}
private void unsuspendTestPackage() {
final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
- PACKAGES_TO_SUSPEND, false, null, null, null);
+ PACKAGES_TO_SUSPEND, false, null, null, (SuspendDialogInfo) null);
assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
}
@@ -318,7 +319,8 @@
@Test
public void testCannotSuspendSelf() {
final String[] unchangedPkgs = mPackageManager.setPackagesSuspended(
- new String[]{mContext.getOpPackageName()}, true, null, null, null);
+ new String[]{mContext.getOpPackageName()}, true, null, null,
+ (SuspendDialogInfo) null);
assertTrue(unchangedPkgs.length == 1);
assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]);
}
@@ -457,7 +459,8 @@
mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED,
ACTION_REPORT_TEST_ACTIVITY_STARTED);
final String testMessage = "This is a test message to report suspension of %1$s";
- suspendTestPackage(null, null, testMessage);
+ suspendTestPackage(null, null,
+ new SuspendDialogInfo.Builder().setMessage(testMessage).build());
startTestAppActivity();
assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2));
assertNotNull("Given dialog message not shown", mUiDevice.wait(
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 869f8fa0..caaa0bb 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -43,6 +43,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
@@ -92,6 +93,8 @@
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
+ private static final String PACKAGE_UNKNOWN = "com.example.unknown";
+
private static final String ADMIN_PKG = "com.android.admin";
private static final String ADMIN_PKG2 = "com.android.admin2";
private static final String ADMIN_PKG3 = "com.android.admin3";
@@ -106,6 +109,9 @@
// Short STABLE_CHARGING_THRESHOLD for testing purposes
private static final long STABLE_CHARGING_THRESHOLD = 2000;
+ /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
+ private static boolean isPackageInstalled = true;
+
private MyInjector mInjector;
private AppStandbyController mController;
@@ -183,6 +189,12 @@
}
@Override
+ boolean isPackageInstalled(String packageName, int flags, int userId) {
+ // Should always return true (default value) unless testing for an uninstalled app
+ return isPackageInstalled;
+ }
+
+ @Override
int[] getRunningUserIds() {
return new int[] {USER_ID};
}
@@ -403,30 +415,30 @@
false));
}
- private void reportEvent(AppStandbyController controller, int eventType,
- long elapsedTime) {
+ private void reportEvent(AppStandbyController controller, int eventType, long elapsedTime,
+ String packageName) {
// Back to ACTIVE on event
mInjector.mElapsedRealtime = elapsedTime;
UsageEvents.Event ev = new UsageEvents.Event();
- ev.mPackage = PACKAGE_1;
+ ev.mPackage = packageName;
ev.mEventType = eventType;
controller.reportEvent(ev, elapsedTime, USER_ID);
}
- private int getStandbyBucket(AppStandbyController controller) {
- return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+ private int getStandbyBucket(AppStandbyController controller, String packageName) {
+ return controller.getAppStandbyBucket(packageName, USER_ID, mInjector.mElapsedRealtime,
true);
}
private void assertBucket(int bucket) {
- assertEquals(bucket, getStandbyBucket(mController));
+ assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
}
@Test
public void testBuckets() throws Exception {
assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
// ACTIVE bucket
assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
@@ -443,7 +455,7 @@
// RARE bucket
assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
- reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1);
+ reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1);
assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
@@ -452,12 +464,48 @@
}
@Test
+ public void testSetAppStandbyBucket() throws Exception {
+ // For a known package, standby bucket should be set properly
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_TIMEOUT, HOUR_MS);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+
+ // For an unknown package, standby bucket should not be set, hence NEVER is returned
+ // Ensure the unknown package is not already in history by removing it
+ mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
+ isPackageInstalled = false; // Mock package is not installed
+ mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_TIMEOUT, HOUR_MS);
+ isPackageInstalled = true; // Reset mocked variable for other tests
+ assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+ }
+
+ @Test
+ public void testAppStandbyBucketOnInstallAndUninstall() throws Exception {
+ // On package install, standby bucket should be ACTIVE
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_UNKNOWN);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+
+ // On uninstall, package should not exist in history and should return a NEVER bucket
+ mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
+ assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+ // Ensure uninstalled app is not in history
+ List<AppStandbyInfo> buckets = mController.getAppStandbyBuckets(USER_ID);
+ for(AppStandbyInfo bucket : buckets) {
+ if (bucket.mPackageName.equals(PACKAGE_UNKNOWN)) {
+ fail("packageName found in app idle history after uninstall.");
+ }
+ }
+ }
+
+ @Test
public void testScreenTimeAndBuckets() throws Exception {
mInjector.setDisplayOn(false);
assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
// ACTIVE bucket
assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
@@ -468,7 +516,7 @@
// RARE bucket, should fail because the screen wasn't ON.
mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
mController.checkIdleStates(USER_ID);
- assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+ assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
mInjector.setDisplayOn(true);
assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
@@ -477,7 +525,7 @@
@Test
public void testForcedIdle() throws Exception {
mController.forceIdleState(PACKAGE_1, USER_ID, true);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
mController.forceIdleState(PACKAGE_1, USER_ID, false);
@@ -488,35 +536,35 @@
@Test
public void testNotificationEvent() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
mInjector.mElapsedRealtime = 1;
- reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
mController.forceIdleState(PACKAGE_1, USER_ID, true);
- reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
}
@Test
public void testSlicePinnedEvent() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
mInjector.mElapsedRealtime = 1;
- reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
mController.forceIdleState(PACKAGE_1, USER_ID, true);
- reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+ reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
}
@Test
public void testSlicePinnedPrivEvent() throws Exception {
mController.forceIdleState(PACKAGE_1, USER_ID, true);
- reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
@Test
@@ -524,28 +572,28 @@
// Set it to timeout or usage, so that prediction can override it
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT, HOUR_MS);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED, HOUR_MS);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
// Fast forward 12 hours
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
mController.checkIdleStates(USER_ID);
// Should still be in predicted bucket, since prediction timeout is 1 day since prediction
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
// Fast forward two more hours
mInjector.mElapsedRealtime += 2 * HOUR_MS;
mController.checkIdleStates(USER_ID);
// Should have now applied prediction timeout
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
// Fast forward RARE bucket
mInjector.mElapsedRealtime += RARE_THRESHOLD;
mController.checkIdleStates(USER_ID);
// Should continue to apply prediction timeout
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
}
@Test
@@ -553,33 +601,33 @@
// Can force to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_FORCED, 1 * HOUR_MS);
- assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_FORCED, 1 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED, 1 * HOUR_MS);
- assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't override NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_DEFAULT, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED, 2 * HOUR_MS);
- assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_PREDICTED, 2 * HOUR_MS);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
@Test
public void testTimeout() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
mInjector.mElapsedRealtime = 2000;
@@ -601,10 +649,10 @@
@Test
public void testCascadingTimeouts() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
- reportEvent(mController, NOTIFICATION_SEEN, 1000);
+ reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
@@ -622,14 +670,15 @@
@Test
public void testOverlappingTimeouts() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
- reportEvent(mController, NOTIFICATION_SEEN, 1000);
+ reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Overlapping USER_INTERACTION before previous one times out
- reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000);
+ reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000,
+ PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Still in ACTIVE after first USER_INTERACTION times out
@@ -654,14 +703,14 @@
public void testSystemInteractionTimeout() throws Exception {
setChargingState(mController, false);
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
// Fast forward to RARE
mInjector.mElapsedRealtime = RARE_THRESHOLD + 100;
mController.checkIdleStates(USER_ID);
assertBucket(STANDBY_BUCKET_RARE);
// Trigger a SYSTEM_INTERACTION and verify bucket
- reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
+ reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Verify it's still in ACTIVE close to end of timeout
@@ -677,11 +726,11 @@
@Test
public void testPredictionNotOverridden() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000;
- reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Falls back to WORKING_SET
@@ -703,7 +752,7 @@
@Test
public void testPredictionStrikesBack() throws Exception {
- reportEvent(mController, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Predict to FREQUENT
@@ -714,7 +763,7 @@
// Add a short timeout event
mInjector.mElapsedRealtime += 1000;
- reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
+ reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
mInjector.mElapsedRealtime += 1000;
mController.checkIdleStates(USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 3dcdd23..cb8ca7e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -39,6 +39,7 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doNothing;
@@ -288,34 +289,25 @@
final WindowTestUtils.TestAppWindowToken token =
WindowTestUtils.createTestAppWindowToken(dc0);
task0.addChild(token, 0);
- dc0.mTapDetector = new TaskTapPointerEventListener(sWm, dc0);
- sWm.registerPointerEventListener(dc0.mTapDetector);
+ dc0.configureDisplayPolicy();
+ assertNotNull(dc0.mTapDetector);
+
final TaskStack stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
final WindowTestUtils.TestAppWindowToken token1 =
WindowTestUtils.createTestAppWindowToken(dc0);
task1.addChild(token1, 0);
- dc1.mTapDetector = new TaskTapPointerEventListener(sWm, dc0);
- sWm.registerPointerEventListener(dc1.mTapDetector);
+ dc1.configureDisplayPolicy();
+ assertNotNull(dc1.mTapDetector);
- // tap on primary display (by sending ACTION_DOWN followed by ACTION_UP)
- DisplayMetrics dm0 = dc0.getDisplayMetrics();
- dc0.mTapDetector.onPointerEvent(
- createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, true));
- dc0.mTapDetector.onPointerEvent(
- createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false));
-
+ // tap on primary display.
+ tapOnDisplay(dc0);
// Check focus is on primary display.
assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
dc0.findFocusedWindow());
- // Tap on secondary display
- DisplayMetrics dm1 = dc1.getDisplayMetrics();
- dc1.mTapDetector.onPointerEvent(
- createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, true));
- dc1.mTapDetector.onPointerEvent(
- createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false));
-
+ // Tap on secondary display.
+ tapOnDisplay(dc1);
// Check focus is on secondary.
assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
dc1.findFocusedWindow());
@@ -626,17 +618,32 @@
return result;
}
- private MotionEvent createTapEvent(float x, float y, boolean isDownEvent) {
+ private void tapOnDisplay(final DisplayContent dc) {
+ final DisplayMetrics dm = dc.getDisplayMetrics();
+ final float x = dm.widthPixels / 2;
+ final float y = dm.heightPixels / 2;
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis() + 100;
- final int metaState = 0;
-
- return MotionEvent.obtain(
+ // sending ACTION_DOWN
+ final MotionEvent downEvent = MotionEvent.obtain(
downTime,
- eventTime,
- isDownEvent ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP,
+ downTime,
+ MotionEvent.ACTION_DOWN,
x,
y,
- metaState);
+ 0 /*metaState*/);
+ downEvent.setDisplayId(dc.getDisplayId());
+ dc.mTapDetector.onPointerEvent(downEvent);
+
+ // sending ACTION_UP
+ final MotionEvent upEvent = MotionEvent.obtain(
+ downTime,
+ eventTime,
+ MotionEvent.ACTION_UP,
+ x,
+ y,
+ 0 /*metaState*/);
+ upEvent.setDisplayId(dc.getDisplayId());
+ dc.mTapDetector.onPointerEvent(upEvent);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
index 07eafa5..a028d5ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -18,23 +18,34 @@
package com.android.server.wm;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.app.WindowConfiguration;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.policy.WindowManagerPolicy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
+/**
+ * Tests for the {@link DisplaySettings} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.DisplaySettingsTests
+ */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -155,6 +166,71 @@
assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
}
+ @Test
+ public void testDefaultToFreeUserRotation() {
+ mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
+ assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, rotation.getUserRotationMode());
+ assertFalse(rotation.isRotationFrozen());
+ }
+
+ @Test
+ public void testDefaultTo0DegRotation() {
+ mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ assertEquals(Surface.ROTATION_0, mSecondaryDisplay.getDisplayRotation().getUserRotation());
+ }
+
+ @Test
+ public void testPersistUserRotationModeInSameInstance() {
+ mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_90);
+
+ mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode());
+ assertTrue(rotation.isRotationFrozen());
+ }
+
+ @Test
+ public void testPersistUserRotationInSameInstance() {
+ mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_90);
+
+ mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ assertEquals(Surface.ROTATION_90, mSecondaryDisplay.getDisplayRotation().getUserRotation());
+ }
+
+ @Test
+ public void testPersistUserRotationModeAcrossInstances() {
+ mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_270);
+ mTarget.writeSettingsLocked();
+
+ DisplaySettings target = new DisplaySettings(sWm, mTestFolder);
+ target.readSettingsLocked();
+
+ target.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
+ assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode());
+ assertTrue(rotation.isRotationFrozen());
+ }
+
+ @Test
+ public void testPersistUserRotationAcrossInstances() {
+ mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+ Surface.ROTATION_270);
+
+ mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+ assertEquals(Surface.ROTATION_270,
+ mSecondaryDisplay.getDisplayRotation().getUserRotation());
+ }
+
private static void assertOverscan(DisplayContent display, int left, int top, int right,
int bottom) {
final DisplayInfo info = display.getDisplayInfo();
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 60025f0..ae40f7e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -334,7 +334,7 @@
final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
waitForIdle();
- assertEquals(displayId, activity.getDisplay().getDisplayId());
+ assertEquals(displayId, activity.getDisplayId());
return activity;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index ced0847..f8e7403 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -30,6 +30,7 @@
import android.platform.test.annotations.Presubmit;
import android.view.InputChannel;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -43,6 +44,7 @@
* atest com.android.server.wm.TaskPositioningControllerTests
*/
@SmallTest
+@FlakyTest(bugId = 117924387)
@RunWith(AndroidJUnit4.class)
@Presubmit
public class TaskPositioningControllerTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 474e5b7..5159551 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -432,17 +432,6 @@
}
@Override
- public int getUserRotationMode() {
- return 0;
- }
-
- @Override
- public void setUserRotationMode(int mode,
- int rotation) {
-
- }
-
- @Override
public int adjustSystemUiVisibilityLw(int visibility) {
return 0;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 54fd7db..389eba5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -34,8 +34,8 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.view.InputChannel;
-
-import androidx.test.InstrumentationRegistry;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerService;
@@ -46,6 +46,12 @@
import org.junit.runners.model.Statement;
import org.mockito.invocation.InvocationOnMock;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+
/**
* A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
* to properly tear it down after.
@@ -61,6 +67,12 @@
private WindowManagerService mService;
private TestWindowManagerPolicy mPolicy;
+ // Record all {@link SurfaceControl.Transaction} created while testing and releases native
+ // resources when test finishes.
+ private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
+ // Record all {@link SurfaceControl} created while testing and releases native resources when
+ // test finishes.
+ private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>();
@Override
public Statement apply(Statement base, Description description) {
@@ -108,12 +120,25 @@
// InputChannel is final and can't be mocked.
InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
if (input != null && input.length > 1) {
- doReturn(input[1]).when(ims).monitorInput(anyString());
+ doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
}
mService = WindowManagerService.main(context, ims, false,
false, mPolicy = new TestWindowManagerPolicy(
WindowManagerServiceRule.this::getWindowManagerService));
+ mService.mTransactionFactory = () -> {
+ final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ mSurfaceTransactions.add(new WeakReference<>(transaction));
+ return transaction;
+ };
+ mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) {
+ @Override
+ public SurfaceControl build() {
+ final SurfaceControl control = super.build();
+ mSurfaceControls.add(new WeakReference<>(control));
+ return control;
+ }
+ };
mService.onInitReady();
@@ -135,6 +160,8 @@
private void tearDown() {
waitUntilWindowManagerHandlersIdle();
+ destroyAllSurfaceTransactions();
+ destroyAllSurfaceControls();
removeServices();
mService = null;
mPolicy = null;
@@ -158,4 +185,24 @@
SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
}
}
+
+ private void destroyAllSurfaceTransactions() {
+ for (final WeakReference<Transaction> reference : mSurfaceTransactions) {
+ final Transaction transaction = reference.get();
+ if (transaction != null) {
+ reference.clear();
+ transaction.close();
+ }
+ }
+ }
+
+ private void destroyAllSurfaceControls() {
+ for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
+ final SurfaceControl control = reference.get();
+ if (control != null) {
+ reference.clear();
+ control.destroy();
+ }
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index a610e6e..3a8c4ae 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -98,10 +98,13 @@
super(s);
}
+ @Override
public SurfaceControl.Builder setParent(SurfaceControl sc) {
mPendingParent = sc;
return super.setParent(sc);
}
+
+ @Override
public SurfaceControl build() {
SurfaceControl sc = super.build();
mParentFor.put(sc, mPendingParent);
@@ -110,7 +113,7 @@
}
}
- class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+ private class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
public SurfaceControl.Builder make(SurfaceSession s) {
return new HierarchyRecorder(s);
}
@@ -131,6 +134,7 @@
@After
public void after() {
mTransaction.close();
+ mParentFor.keySet().forEach(SurfaceControl::destroy);
mParentFor.clear();
}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
index 7e7decf..ab222b9 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
@@ -20,7 +20,7 @@
LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_STATIC_JAVA_LIBRARIES := androidx-test ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4e007c2d..3266b8b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1689,7 +1689,7 @@
}
@Test
- public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
+ public void testGetNotificationChannelFromPrivilegedListener_cdm_success() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
@@ -1703,7 +1703,7 @@
}
@Test
- public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
+ public void testGetNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1721,6 +1721,38 @@
}
@Test
+ public void testGetNotificationChannelFromPrivilegedListener_assistant_success()
+ throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+
+ mBinderService.getNotificationChannelsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
+
+ verify(mPreferencesHelper, times(1)).getNotificationChannels(
+ anyString(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+ try {
+ mBinderService.getNotificationChannelsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
+ fail("listeners that don't have a companion device shouldn't be able to call this");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mPreferencesHelper, never()).getNotificationChannels(
+ anyString(), anyInt(), anyBoolean());
+ }
+
+ @Test
public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
index 0f8b18a..c095ae0 100644
--- a/services/tests/wmtests/Android.mk
+++ b/services/tests/wmtests/Android.mk
@@ -14,7 +14,7 @@
$(call all-java-files-under, ../servicestests/utils)
LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx-test \
+ androidx.test.runner \
mockito-target-minus-junit4 \
platform-test-annotations \
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 4e99732..bc54a5d 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -320,14 +320,7 @@
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
- if (appUsageHistory == null) {
- return false; // Default to not idle
- } else {
- return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
- // Whether or not it's passed will now be externally calculated and the
- // bucket will be pushed to the history using setAppStandbyBucket()
- //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
- }
+ return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
}
public AppUsageHistory getAppUsageHistory(String packageName, int userId,
@@ -404,17 +397,19 @@
public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
- getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ getPackageHistory(userHistory, packageName, elapsedRealtime, false);
// Don't adjust the default, else it'll wrap around to a positive value
- if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE;
+ if (appUsageHistory == null || appUsageHistory.lastJobRunTime == Long.MIN_VALUE) {
+ return Long.MAX_VALUE;
+ }
return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
}
public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
- getPackageHistory(userHistory, packageName, elapsedRealtime, true);
- return appUsageHistory.currentBucket;
+ getPackageHistory(userHistory, packageName, elapsedRealtime, false);
+ return appUsageHistory == null ? STANDBY_BUCKET_NEVER : appUsageHistory.currentBucket;
}
public ArrayList<AppStandbyInfo> getAppStandbyBuckets(int userId, boolean appIdleEnabled) {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 02ad3a8..6a74564 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -1161,6 +1161,10 @@
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime, boolean resetTimeout) {
synchronized (mAppIdleLock) {
+ // If the package is not installed, don't allow the bucket to be set.
+ if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+ return;
+ }
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
@@ -1594,6 +1598,10 @@
return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
}
+ boolean isPackageInstalled(String packageName, int flags, int userId) {
+ return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
+ }
+
int[] getRunningUserIds() throws RemoteException {
return ActivityManager.getService().getRunningUserIds();
}
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 5bf94af..32fc796 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -75,14 +75,21 @@
if (uid != Process.SYSTEM_UID) {
enforcePackageBelongsToUid(uid, packageName);
- PackageInfo pkg;
+ int packageTargetSdkVersion;
+ long token = Binder.clearCallingIdentity();
try {
- pkg = mContext.getPackageManager().getPackageInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new RemoteException("package " + packageName + " cannot be found");
+ PackageInfo pkg;
+ try {
+ pkg = mContext.getPackageManager().getPackageInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RemoteException("package " + packageName + " cannot be found");
+ }
+ packageTargetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ if (packageTargetSdkVersion >= Build.VERSION_CODES.Q) {
if (mContext.checkPermission(android.Manifest.permission.MANAGE_USB, pid, uid)
== PackageManager.PERMISSION_DENIED) {
UsbUserSettingsManager settings = mSettingsManager.getSettingsForUser(
diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/TEST_MAPPING
new file mode 100644
index 0000000..8c9d4df
--- /dev/null
+++ b/startop/iorap/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "libiorap-java-tests"
+ }
+ ],
+ "imports": [
+ {
+ "path": "system/iorap"
+ }
+ ]
+}
diff --git a/startop/iorap/tests/AndroidTest.xml b/startop/iorap/tests/AndroidTest.xml
new file mode 100644
index 0000000..f83a16e
--- /dev/null
+++ b/startop/iorap/tests/AndroidTest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration description="Runs libiorap-java-tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="libiorap-java-tests.apk" />
+ </target_preparer>
+
+ <!--
+ Our IIorapIntegrationTest.kt requires setlinux to be disabled:
+ it connects to the iorapd binder service but this requires selinux permissions:
+
+ avc: denied { find } for service=iorapd pid=2738 uid=10050
+ scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:iorapd_service:s0
+ tclass=service_manager permissive=0
+ -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer">
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.google.android.startop.iorap.tests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
+
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
index 4ba44a9..16dcbe2 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -77,17 +77,21 @@
inOrder.verifyNoMoreInteractions()
} finally {
- iorapService.setTaskListener(null)
+ // iorapService.setTaskListener(null)
+ // FIXME: null is broken, C++ side sees a non-null object.
}
}
@Test
fun testOnPackageEvent() {
+ /*
testAnyMethod { requestId : RequestId ->
iorapService.onPackageEvent(requestId,
PackageEvent.createReplaced(
Uri.parse("https://www.google.com"), "com.fake.package"))
}
+ */
+ // FIXME: Broken for some reason. C++ side never sees this call.
}
@Test
diff --git a/startop/tools/view_compiler/Android.bp b/startop/tools/view_compiler/Android.bp
index c3e9184..3681529 100644
--- a/startop/tools/view_compiler/Android.bp
+++ b/startop/tools/view_compiler/Android.bp
@@ -14,19 +14,30 @@
// limitations under the License.
//
+cc_defaults {
+ name: "viewcompiler_defaults",
+ shared_libs: [
+ "libdexfile",
+ "slicer",
+ ],
+}
+
cc_library_host_static {
name: "libviewcompiler",
+ defaults: ["viewcompiler_defaults"],
srcs: [
+ "dex_builder.cc",
"java_lang_builder.cc",
"util.cc",
],
static_libs: [
- "libbase"
- ]
+ "libbase",
+ ],
}
cc_binary_host {
name: "viewcompiler",
+ defaults: ["viewcompiler_defaults"],
srcs: [
"main.cc",
],
@@ -40,10 +51,12 @@
cc_test_host {
name: "view-compiler-tests",
+ defaults: ["viewcompiler_defaults"],
srcs: [
+ "dex_builder_test.cc",
"util_test.cc",
],
static_libs: [
"libviewcompiler",
- ]
+ ],
}
diff --git a/startop/tools/view_compiler/dex_builder.cc b/startop/tools/view_compiler/dex_builder.cc
new file mode 100644
index 0000000..7a9f41f
--- /dev/null
+++ b/startop/tools/view_compiler/dex_builder.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_builder.h"
+
+#include "dex/descriptors_names.h"
+#include "dex/dex_instruction.h"
+
+#include <fstream>
+#include <memory>
+
+namespace startop {
+namespace dex {
+
+using std::shared_ptr;
+using std::string;
+
+using art::Instruction;
+using ::dex::kAccPublic;
+
+const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; };
+const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; };
+
+namespace {
+// From https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
+constexpr uint8_t kDexFileMagic[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00};
+
+// Strings lengths can be 32 bits long, but encoded as LEB128 this can take up to five bytes.
+constexpr size_t kMaxEncodedStringLength{5};
+
+} // namespace
+
+void* TrackingAllocator::Allocate(size_t size) {
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
+ void* raw_buffer = buffer.get();
+ allocations_[raw_buffer] = std::move(buffer);
+ return raw_buffer;
+}
+
+void TrackingAllocator::Free(void* ptr) { allocations_.erase(allocations_.find(ptr)); }
+
+// Write out a DEX file that is basically:
+//
+// package dextest;
+// public class DexTest {
+// public static int foo() { return 5; }
+// }
+void WriteTestDexFile(const string& filename) {
+ DexBuilder dex_file;
+
+ ClassBuilder cbuilder{dex_file.MakeClass("dextest.DexTest")};
+ cbuilder.set_source_file("dextest.java");
+
+ MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
+
+ MethodBuilder::Register r = method.MakeRegister();
+ method.BuildConst4(r, 5);
+ method.BuildReturn(r);
+
+ method.Encode();
+
+ slicer::MemView image{dex_file.CreateImage()};
+
+ std::ofstream out_file(filename);
+ out_file.write(image.ptr<const char>(), image.size());
+}
+
+DexBuilder::DexBuilder() : dex_file_{std::make_shared<ir::DexFile>()} {
+ dex_file_->magic = slicer::MemView{kDexFileMagic, sizeof(kDexFileMagic)};
+}
+
+slicer::MemView DexBuilder::CreateImage() {
+ ::dex::Writer writer(dex_file_);
+ size_t image_size{0};
+ ::dex::u1* image = writer.CreateImage(&allocator_, &image_size);
+ return slicer::MemView{image, image_size};
+}
+
+ir::String* DexBuilder::GetOrAddString(const std::string& string) {
+ ir::String*& entry = strings_[string];
+
+ if (entry == nullptr) {
+ // Need to encode the length and then write out the bytes, including 1 byte for null terminator
+ auto buffer = std::make_unique<uint8_t[]>(string.size() + kMaxEncodedStringLength + 1);
+ uint8_t* string_data_start = ::dex::WriteULeb128(buffer.get(), string.size());
+
+ size_t header_length =
+ reinterpret_cast<uintptr_t>(string_data_start) - reinterpret_cast<uintptr_t>(buffer.get());
+
+ auto end = std::copy(string.begin(), string.end(), string_data_start);
+ *end = '\0';
+
+ entry = Alloc<ir::String>();
+ // +1 for null terminator
+ entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
+ string_data_.push_back(std::move(buffer));
+ }
+ return entry;
+}
+
+ClassBuilder DexBuilder::MakeClass(const std::string& name) {
+ auto* class_def = Alloc<ir::Class>();
+ ir::Type* type_def = GetOrAddType(art::DotToDescriptor(name.c_str()));
+ type_def->class_def = class_def;
+
+ class_def->type = type_def;
+ class_def->super_class = GetOrAddType(art::DotToDescriptor("java.lang.Object"));
+ class_def->access_flags = kAccPublic;
+ return ClassBuilder{this, class_def};
+}
+
+// TODO(eholk): we probably want GetOrAddString() also
+ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
+ if (types_by_descriptor_.find(descriptor) != types_by_descriptor_.end()) {
+ return types_by_descriptor_[descriptor];
+ }
+
+ ir::Type* type = Alloc<ir::Type>();
+ type->descriptor = GetOrAddString(descriptor);
+ types_by_descriptor_[descriptor] = type;
+ return type;
+}
+
+ir::Proto* Prototype::Encode(DexBuilder* dex) const {
+ auto* proto = dex->Alloc<ir::Proto>();
+ proto->shorty = dex->GetOrAddString(Shorty());
+ proto->return_type = dex->GetOrAddType(return_type_.descriptor());
+ if (param_types_.size() > 0) {
+ proto->param_types = dex->Alloc<ir::TypeList>();
+ for (const auto& param_type : param_types_) {
+ proto->param_types->types.push_back(dex->GetOrAddType(param_type.descriptor()));
+ }
+ } else {
+ proto->param_types = nullptr;
+ }
+ return proto;
+}
+
+std::string Prototype::Shorty() const {
+ std::string shorty;
+ shorty.append(return_type_.short_descriptor());
+ for (const auto& type_descriptor : param_types_) {
+ shorty.append(type_descriptor.short_descriptor());
+ }
+ return shorty;
+}
+
+ClassBuilder::ClassBuilder(DexBuilder* parent, ir::Class* class_def)
+ : parent_(parent), class_(class_def) {}
+
+MethodBuilder ClassBuilder::CreateMethod(const std::string& name, Prototype prototype) {
+ ir::String* dex_name{parent_->GetOrAddString(name)};
+
+ auto* decl = parent_->Alloc<ir::MethodDecl>();
+ decl->name = dex_name;
+ decl->parent = class_->type;
+ decl->prototype = prototype.Encode(parent_);
+
+ return MethodBuilder{parent_, class_, decl};
+}
+
+void ClassBuilder::set_source_file(const string& source) {
+ class_->source_file = parent_->GetOrAddString(source);
+}
+
+MethodBuilder::MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl)
+ : dex_{dex}, class_{class_def}, decl_{decl} {}
+
+ir::EncodedMethod* MethodBuilder::Encode() {
+ auto* method = dex_->Alloc<ir::EncodedMethod>();
+ method->decl = decl_;
+
+ // TODO: make access flags configurable
+ method->access_flags = kAccPublic | ::dex::kAccStatic;
+
+ auto* code = dex_->Alloc<ir::Code>();
+ code->registers = num_registers_;
+ // TODO: support ins and outs
+ code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
+ method->code = code;
+
+ class_->direct_methods.push_back(method);
+
+ return method;
+}
+
+MethodBuilder::Register MethodBuilder::MakeRegister() { return num_registers_++; }
+
+void MethodBuilder::BuildReturn() { buffer_.push_back(Instruction::RETURN_VOID); }
+
+void MethodBuilder::BuildReturn(Register src) { buffer_.push_back(Instruction::RETURN | src << 8); }
+
+void MethodBuilder::BuildConst4(Register target, int value) {
+ DCHECK_LT(value, 16);
+ // TODO: support more registers
+ DCHECK_LT(target, 16);
+ buffer_.push_back(Instruction::CONST_4 | (value << 12) | (target << 8));
+}
+
+} // namespace dex
+} // namespace startop
diff --git a/startop/tools/view_compiler/dex_builder.h b/startop/tools/view_compiler/dex_builder.h
new file mode 100644
index 0000000..d280abc
--- /dev/null
+++ b/startop/tools/view_compiler/dex_builder.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DEX_BUILDER_H_
+#define DEX_BUILDER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "slicer/dex_ir.h"
+#include "slicer/writer.h"
+
+namespace startop {
+namespace dex {
+
+// TODO: remove this once the dex generation code is complete.
+void WriteTestDexFile(const std::string& filename);
+
+//////////////////////////
+// Forward declarations //
+//////////////////////////
+class DexBuilder;
+
+// Our custom allocator for dex::Writer
+//
+// This keeps track of all allocations and ensures they are freed when
+// TrackingAllocator is destroyed. Pointers to memory allocated by this
+// allocator must not outlive the allocator.
+class TrackingAllocator : public ::dex::Writer::Allocator {
+ public:
+ virtual void* Allocate(size_t size);
+ virtual void Free(void* ptr);
+
+ private:
+ std::map<void*, std::unique_ptr<uint8_t[]>> allocations_;
+};
+
+// Represents a DEX type descriptor.
+//
+// TODO: add a way to create a descriptor for a reference of a class type.
+class TypeDescriptor {
+ public:
+ // Named constructors for base type descriptors.
+ static const TypeDescriptor Int();
+ static const TypeDescriptor Void();
+
+ // Return the full descriptor, such as I or Ljava/lang/Object
+ const std::string& descriptor() const { return descriptor_; }
+ // Return the shorty descriptor, such as I or L
+ std::string short_descriptor() const { return descriptor().substr(0, 1); }
+
+ private:
+ TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
+
+ const std::string descriptor_;
+};
+
+// Defines a function signature. For example, Prototype{TypeDescriptor::VOID, TypeDescriptor::Int}
+// represents the function type (Int) -> Void.
+class Prototype {
+ public:
+ template <typename... TypeDescriptors>
+ Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
+ : return_type_{return_type}, param_types_{param_types...} {}
+
+ // Encode this prototype into the dex file.
+ ir::Proto* Encode(DexBuilder* dex) const;
+
+ // Get the shorty descriptor, such as VII for (Int, Int) -> Void
+ std::string Shorty() const;
+
+ private:
+ const TypeDescriptor return_type_;
+ const std::vector<TypeDescriptor> param_types_;
+};
+
+// Tools to help build methods and their bodies.
+class MethodBuilder {
+ public:
+ MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl);
+
+ // Encode the method into DEX format.
+ ir::EncodedMethod* Encode();
+
+ // Registers are just represented by their number.
+ using Register = size_t;
+
+ // Create a new register to be used to storing values. Note that these are not SSA registers, like
+ // might be expected in similar code generators. This does no liveness tracking or anything, so
+ // it's up to the caller to reuse registers as appropriate.
+ Register MakeRegister();
+
+ /////////////////////////////////
+ // Instruction builder methods //
+ /////////////////////////////////
+
+ // return-void
+ void BuildReturn();
+ void BuildReturn(Register src);
+ // const/4
+ void BuildConst4(Register target, int value);
+
+ // TODO: add builders for more instructions
+
+ private:
+ DexBuilder* dex_;
+ ir::Class* class_;
+ ir::MethodDecl* decl_;
+
+ // A buffer to hold instructions we are generating.
+ std::vector<::dex::u2> buffer_;
+
+ // How many registers we've allocated
+ size_t num_registers_;
+};
+
+// A helper to build class definitions.
+class ClassBuilder {
+ public:
+ ClassBuilder(DexBuilder* parent, ir::Class* class_def);
+
+ void set_source_file(const std::string& source);
+
+ // Create a method with the given name and prototype. The returned MethodBuilder can be used to
+ // fill in the method body.
+ MethodBuilder CreateMethod(const std::string& name, Prototype prototype);
+
+ private:
+ DexBuilder* parent_;
+ ir::Class* class_;
+};
+
+// Builds Dex files from scratch.
+class DexBuilder {
+ public:
+ DexBuilder();
+
+ // Create an in-memory image of the DEX file that can either be loaded directly or written to a
+ // file.
+ slicer::MemView CreateImage();
+
+ template <typename T>
+ T* Alloc() {
+ return dex_file_->Alloc<T>();
+ }
+
+ // Find the ir::String that matches the given string, creating it if it does not exist.
+ ir::String* GetOrAddString(const std::string& string);
+ // Create a new class of the given name.
+ ClassBuilder MakeClass(const std::string& name);
+
+ // Add a type for the given descriptor, or return the existing one if it already exists.
+ // See the TypeDescriptor class for help generating these.
+ ir::Type* GetOrAddType(const std::string& descriptor);
+
+ private:
+ std::shared_ptr<ir::DexFile> dex_file_;
+
+ // allocator_ is needed to be able to encode the image.
+ TrackingAllocator allocator_;
+
+ // We'll need to allocate buffers for all of the encoded strings we create. This is where we store
+ // all of them.
+ std::vector<std::unique_ptr<uint8_t[]>> string_data_;
+
+ // Keep track of what types we've defined so we can look them up later.
+ std::map<std::string, ir::Type*> types_by_descriptor_;
+
+ // Keep track of what strings we've defined so we can look them up later.
+ std::map<std::string, ir::String*> strings_;
+};
+
+} // namespace dex
+} // namespace startop
+
+#endif // DEX_BUILDER_H_
diff --git a/startop/tools/view_compiler/dex_builder_test.cc b/startop/tools/view_compiler/dex_builder_test.cc
new file mode 100644
index 0000000..0d8b854
--- /dev/null
+++ b/startop/tools/view_compiler/dex_builder_test.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_builder.h"
+
+#include "dex/art_dex_file_loader.h"
+#include "dex/dex_file.h"
+#include "gtest/gtest.h"
+
+using namespace startop::dex;
+
+// Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and
+// returns whether the verification was successful.
+bool EncodeAndVerify(DexBuilder* dex_file) {
+ slicer::MemView image{dex_file->CreateImage()};
+
+ art::ArtDexFileLoader loader;
+ std::string error_msg;
+ std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(),
+ image.size(),
+ /*location=*/"",
+ /*location_checksum=*/0,
+ /*oat_dex_file=*/nullptr,
+ /*verify=*/true,
+ /*verify_checksum=*/false,
+ &error_msg)};
+ return loaded_dex_file != nullptr;
+}
+
+TEST(DexBuilderTest, VerifyDexWithClassMethod) {
+ DexBuilder dex_file;
+
+ auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+ auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})};
+ method.BuildReturn();
+ method.Encode();
+
+ EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
+
+// Makes sure a bad DEX class fails to verify.
+TEST(DexBuilderTest, VerifyBadDexWithClassMethod) {
+ DexBuilder dex_file;
+
+ auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+ // This method has the error, because methods cannot take Void() as a parameter.
+ auto method{
+ cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})};
+ method.BuildReturn();
+ method.Encode();
+
+ EXPECT_FALSE(EncodeAndVerify(&dex_file));
+}
+
+TEST(DexBuilderTest, VerifyDexReturn5) {
+ DexBuilder dex_file;
+
+ auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+ auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
+ auto r = method.MakeRegister();
+ method.BuildConst4(r, 5);
+ method.BuildReturn(r);
+ method.Encode();
+
+ EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
diff --git a/startop/tools/view_compiler/main.cc b/startop/tools/view_compiler/main.cc
index 0ad7e24..7d791c2 100644
--- a/startop/tools/view_compiler/main.cc
+++ b/startop/tools/view_compiler/main.cc
@@ -16,6 +16,7 @@
#include "gflags/gflags.h"
+#include "dex_builder.h"
#include "java_lang_builder.h"
#include "util.h"
@@ -27,15 +28,17 @@
#include <string>
#include <vector>
+namespace {
+
using namespace tinyxml2;
using std::string;
constexpr char kStdoutFilename[]{"stdout"};
-DEFINE_string(package, "", "The package name for the generated class (required)");
+DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
+DEFINE_string(package, "", "The package name for the generated class (required)");
-namespace {
class ViewCompilerXmlVisitor : public XMLVisitor {
public:
ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
@@ -63,6 +66,7 @@
private:
JavaLangViewBuilder* builder_;
};
+
} // end namespace
int main(int argc, char** argv) {
@@ -82,6 +86,11 @@
return 1;
}
+ if (FLAGS_dex) {
+ startop::dex::WriteTestDexFile("test.dex");
+ return 0;
+ }
+
const char* const filename = argv[kFileNameParam];
const string layout_name = FindLayoutNameFromFilename(filename);
@@ -102,4 +111,4 @@
xml.Accept(&visitor);
return 0;
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 8454d12..d9e7167 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.job.JobService;
@@ -2889,100 +2890,131 @@
public static final String SUBSCRIPTION_ID = "sub_id";
/**
- * The profile_id to which the APN saved in modem
+ * The profile_id to which the APN saved in modem.
* <p>Type: INTEGER</p>
*@hide
*/
public static final String PROFILE_ID = "profile_id";
/**
- * Is the apn setting to be set in modem
- * <P>Type: INTEGER (boolean)</P>
+ * If set to {@code true}, then the APN setting will persist to the modem.
+ * <p>Type: INTEGER (boolean)</p>
*@hide
*/
+ @SystemApi
public static final String MODEM_COGNITIVE = "modem_cognitive";
/**
- * The max connections of this apn
+ * The max connections of this APN.
* <p>Type: INTEGER</p>
*@hide
*/
+ @SystemApi
public static final String MAX_CONNS = "max_conns";
/**
- * The wait time for retry of the apn
+ * The wait time for retry of the APN.
* <p>Type: INTEGER</p>
*@hide
*/
+ @SystemApi
public static final String WAIT_TIME = "wait_time";
/**
- * The time to limit max connection for the apn
+ * The time to limit max connection for the APN.
* <p>Type: INTEGER</p>
*@hide
*/
+ @SystemApi
public static final String MAX_CONNS_TIME = "max_conns_time";
/**
- * The MTU size of the mobile interface to which the APN connected
+ * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected.
* <p>Type: INTEGER </p>
* @hide
*/
+ @SystemApi
public static final String MTU = "mtu";
/**
- * Is this APN added/edited/deleted by a user or carrier?
+ * APN edit status. APN could be added/edited/deleted by a user or carrier.
* <p>Type: INTEGER </p>
* @hide
*/
+ @SystemApi
public static final String EDITED = "edited";
/**
- * Is this APN visible to the user?
- * <p>Type: INTEGER (boolean) </p>
+ * {@code true} if this APN visible to the user, {@code false} otherwise.
+ * <p>Type: INTEGER (boolean)</p>
* @hide
*/
+ @SystemApi
public static final String USER_VISIBLE = "user_visible";
/**
- * Is the user allowed to edit this APN?
- * <p>Type: INTEGER (boolean) </p>
+ * {@code true} if the user allowed to edit this APN, {@code false} otherwise.
+ * <p>Type: INTEGER (boolean)</p>
* @hide
*/
+ @SystemApi
public static final String USER_EDITABLE = "user_editable";
/**
- * Following are possible values for the EDITED field
+ * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to
+ * edit.
+ * <p>Type: INTEGER </p>
* @hide
*/
+ @SystemApi
public static final int UNEDITED = 0;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} indicates that this APN has been edited by users.
+ * <p>Type: INTEGER </p>
+ * @hide
*/
+ @SystemApi
public static final int USER_EDITED = 1;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} indicates that this APN has been deleted by users.
+ * <p>Type: INTEGER </p>
+ * @hide
*/
+ @SystemApi
public static final int USER_DELETED = 2;
+
/**
- * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
- * by the user is still present in the new APN database and therefore must remain tagged
- * as user deleted rather than completely removed from the database
+ * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+ * deleted by the user is still present in the new APN database and therefore must remain
+ * tagged as user deleted rather than completely removed from the database.
* @hide
*/
public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers.
+ * <p>Type: INTEGER </p>
+ * @hide
*/
+ @SystemApi
public static final int CARRIER_EDITED = 4;
+
/**
- * CARRIER_DELETED values are currently not used as there is no usecase. If they are used,
+ * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers.
+ * CARRIER_DELETED values are currently not used as there is no use case. If they are used,
* delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+ * <p>Type: INTEGER </p>
* @hide
*/
public static final int CARRIER_DELETED = 5;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+ * deleted by the carrier is still present in the new APN database and therefore must remain
+ * tagged as user deleted rather than completely removed from the database.
+ * @hide
*/
public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
@@ -3011,16 +3043,20 @@
* The APN set id. When the user manually selects an APN or the framework sets an APN as
* preferred, all APNs with the same set id as the selected APN should be prioritized over
* APNs in other sets.
+ * <p>Type: INTEGER</p>
* @hide
*/
+ @SystemApi
public static final String APN_SET_ID = "apn_set_id";
/**
- * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
- * user manually selects an APN with no set set, there is no need to prioritize any specific
- * APN set ids.
+ * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a
+ * set. If the user manually selects an APN with no set set, there is no need to prioritize
+ * any specific APN set ids.
+ * <p>Type: INTEGER</p>
* @hide
*/
+ @SystemApi
public static final int NO_SET_SET = 0;
}
@@ -3524,6 +3560,18 @@
public static final String CARRIER_ID = "carrier_id";
/**
+ * A unique mno carrier id. mno carrier shares the same {@link All#MCCMNC} as carrier id
+ * and can be solely identified by {@link All#MCCMNC} only. If there is no such mno
+ * carrier, then mno carrier id equals to {@link #CARRIER_ID carrier id}.
+ *
+ * <p>mno carrier id can be used as fallback id. When the exact carrier id configurations
+ * are not found, usually fall back to its mno carrier id.
+ * <P>Type: INTEGER </P>
+ * @hide
+ */
+ public static final String MNO_CARRIER_ID = "mno_carrier_id";
+
+ /**
* Contains mappings between matching rules with carrier id for all carriers.
* @hide
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fac1943..b0997f1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1202,6 +1202,20 @@
"always_show_data_rat_icon_bool";
/**
+ * Boolean indicating if default data account should show LTE or 4G icon
+ * @hide
+ */
+ public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL =
+ "show_4g_for_lte_data_icon_bool";
+
+ /**
+ * Boolean indicating if lte+ icon should be shown if available
+ * @hide
+ */
+ public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL =
+ "hide_lte_plus_data_icon_bool";
+
+ /**
* Boolean to decide whether to show precise call failed cause to user
* @hide
*/
@@ -1965,6 +1979,31 @@
public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
/**
+ * Indicates if the carrier supports auto-upgrading a call to RTT when receiving a call from a
+ * RTT-supported device.
+ * @hide
+ */
+ public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
+
+ /**
+ * Indicates if the carrier supports RTT during a video call.
+ * @hide
+ */
+ public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+
+ /**
+ * Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
+ * @hide
+ */
+ public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
+
+ /**
+ * Indicates if the carrier supports downgrading a RTT call to a voice call during the call.
+ * @hide
+ */
+ public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
+
+ /**
* The flag to disable the popup dialog which warns the user of data charges.
* @hide
*/
@@ -2505,6 +2544,8 @@
sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true);
+ sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 3ea018af..c83d6aa 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Handler;
@@ -291,6 +292,15 @@
*/
public static final int LISTEN_PREFERRED_DATA_SUBID_CHANGE = 0x00400000;
+ /**
+ * Listen for changes to the radio power state.
+ *
+ * @see #onRadioPowerStateChanged
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -420,6 +430,9 @@
case LISTEN_PREFERRED_DATA_SUBID_CHANGE:
PhoneStateListener.this.onPreferredDataSubIdChanged((int) msg.obj);
break;
+ case LISTEN_RADIO_POWER_STATE_CHANGED:
+ PhoneStateListener.this.onRadioPowerStateChanged((int) msg.obj);
+ break;
}
}
};
@@ -672,6 +685,17 @@
}
/**
+ * Callback invoked when modem radio power state changes. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param state the modem radio power state
+ * @hide
+ */
+ @SystemApi
+ public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -807,6 +831,10 @@
send(LISTEN_PREFERRED_DATA_SUBID_CHANGE, 0, 0, subId);
}
+ public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+ send(LISTEN_RADIO_POWER_STATE_CHANGED, 0, 0, state);
+ }
+
}
/**
diff --git a/telephony/java/android/telephony/RcsManager.java b/telephony/java/android/telephony/RcsManager.java
new file mode 100644
index 0000000..00ce03a
--- /dev/null
+++ b/telephony/java/android/telephony/RcsManager.java
@@ -0,0 +1,52 @@
+/*
+ * 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.telephony;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IRcs;
+
+/**
+ * RcsManager is the application interface to RcsProvider and provides access methods to
+ * RCS related database tables.
+ * @hide - TODO make this public
+ */
+public class RcsManager {
+ private static final String TAG = "RcsManager";
+ private static final boolean VDBG = false;
+
+ /**
+ * Delete the RcsThread identified by the given threadId.
+ * @param threadId threadId of the thread to be deleted.
+ */
+ public void deleteThread(int threadId) {
+ if (VDBG) logd("deleteThread: threadId: " + threadId);
+ try {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+ if (iRcs != null) {
+ iRcs.deleteThread(threadId);
+ }
+ } catch (RemoteException re) {
+
+ }
+ }
+
+ private static void logd(String msg) {
+ Rlog.d(TAG, msg);
+ }
+}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f850693..3b40164 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -88,8 +88,7 @@
/** @hide */
public static final int INVALID_PHONE_INDEX = -1;
- /** An invalid slot identifier */
- /** @hide */
+ /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */
public static final int INVALID_SIM_SLOT_INDEX = -1;
/** Indicates the default subscription ID in Telephony. */
@@ -1310,15 +1309,15 @@
/**
* Get slotIndex associated with the subscription.
- * @return slotIndex as a positive integer or a negative value if an error either
- * SIM_NOT_INSERTED or < 0 if an invalid slot index
- * @hide
+ *
+ * @param subscriptionId the unique SubscriptionInfo index in database
+ * @return slotIndex as a positive integer or {@link #INVALID_SIM_SLOT_INDEX} if the supplied
+ * subscriptionId doesn't have an associated slot index.
*/
- @UnsupportedAppUsage
- public static int getSlotIndex(int subId) {
- if (!isValidSubscriptionId(subId)) {
+ public static int getSlotIndex(int subscriptionId) {
+ if (!isValidSubscriptionId(subscriptionId)) {
if (DBG) {
- logd("[getSlotIndex]- fail");
+ logd("[getSlotIndex]- supplied subscriptionId is invalid.");
}
}
@@ -1327,7 +1326,7 @@
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- result = iSub.getSlotIndex(subId);
+ result = iSub.getSlotIndex(subscriptionId);
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fca14c8..69901d2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17,6 +17,7 @@
package android.telephony;
import static android.content.Context.TELECOM_SERVICE;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
@@ -59,6 +60,7 @@
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.Log;
@@ -705,7 +707,7 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @UnsupportedAppUsage
public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
"android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
@@ -1162,6 +1164,16 @@
public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
/**
+ * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
+ * the updated mno carrier id of the current subscription.
+ * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+ * the carrier cannot be identified.
+ *
+ *@hide
+ */
+ public static final String EXTRA_MNO_CARRIER_ID = "android.telephony.extra.MNO_CARRIER_ID";
+
+ /**
* An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
* indicates the updated carrier name of the current subscription.
* {@see TelephonyManager#getSimCarrierIdName()}
@@ -1220,6 +1232,38 @@
*/
public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
+ /**
+ * Broadcast intent action indicating that the telephony provider DB got lost.
+ *
+ * <p>
+ * The {@link #EXTRA_IS_CORRUPTED} extra indicates whether the database is lost
+ * due to corruption or not
+ *
+ * <p class="note">
+ * Requires the MODIFY_PHONE_STATE permission.
+ *
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ *
+ * @see #EXTRA_IS_CORRUPTED
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public static final String ACTION_MMSSMS_DATABASE_LOST =
+ "android.intent.action.MMSSMS_DATABASE_LOST";
+
+ /**
+ * A boolean extra used with {@link #ACTION_MMSSMS_DATABASE_LOST} to indicate
+ * whether the database is lost due to corruption or not.
+ *
+ * @see #ACTION_MMSSMS_DATABASE_LOST
+ *
+ * @hide
+ */
+ public static final String EXTRA_IS_CORRUPTED = "isCorrupted";
+
//
//
// Device Info
@@ -1267,11 +1311,11 @@
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
* MEID for CDMA.
@@ -1296,11 +1340,11 @@
* Returns the unique device ID of a subscription, for example, the IMEI for
* GSM and the MEID for CDMA phones. Return null if device ID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param slotIndex of which deviceID is returned
*
@@ -1328,11 +1372,11 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // No support for device / profile owner.
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1344,11 +1388,11 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param slotIndex of which IMEI is returned
*/
@@ -1397,11 +1441,11 @@
/**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // No support for device / profile owner.
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1412,11 +1456,11 @@
/**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param slotIndex of which MEID is returned
*/
@@ -1618,8 +1662,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
- return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
- mContext.getApplicationInfo().targetSdkVersion);
+ return telephony.getNeighboringCellInfo(mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1722,7 +1765,7 @@
}
/** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @UnsupportedAppUsage
private int getPhoneTypeFromProperty(int phoneId) {
String type = getTelephonyProperty(phoneId,
TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
@@ -2913,11 +2956,11 @@
* unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2930,11 +2973,11 @@
* unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param subId for which Sim Serial number is returned
* @hide
@@ -3075,11 +3118,11 @@
* Return null if it is unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3093,11 +3136,11 @@
* Return null if it is unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param subId whose subscriber id is returned
* @hide
@@ -6870,6 +6913,60 @@
}
/** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"RADIO_POWER_"},
+ value = {RADIO_POWER_OFF,
+ RADIO_POWER_ON,
+ RADIO_POWER_UNAVAILABLE,
+ })
+ public @interface RadioPowerState {}
+
+ /**
+ * Radio explicitly powered off (e.g, airplane mode).
+ * @hide
+ */
+ @SystemApi
+ public static final int RADIO_POWER_OFF = 0;
+
+ /**
+ * Radio power is on.
+ * @hide
+ */
+ @SystemApi
+ public static final int RADIO_POWER_ON = 1;
+
+ /**
+ * Radio power unavailable (eg, modem resetting or not booted).
+ * @hide
+ */
+ @SystemApi
+ public static final int RADIO_POWER_UNAVAILABLE = 2;
+
+ /**
+ * @return current modem radio state.
+ *
+ * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+ * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE})
+ public @RadioPowerState int getRadioPowerState() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return RADIO_POWER_UNAVAILABLE;
+ }
+
+ /** @hide */
@SystemApi
@SuppressLint("Doclava125")
public void updateServiceLocation() {
@@ -7340,7 +7437,9 @@
@UnsupportedAppUsage
public boolean isVolteAvailable() {
try {
- return getITelephony().isVolteAvailable(getSubId());
+ return getITelephony().isAvailable(getSubId(),
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
} catch (RemoteException | NullPointerException ex) {
return false;
}
@@ -8279,20 +8378,31 @@
}
/**
- * Action set from carrier signalling broadcast receivers to enable/disable metered apns
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
- * @param subId the subscription ID that this action applies to.
- * @param enabled control enable or disable metered apns.
+ * Used to enable or disable carrier data by the system based on carrier signalling or
+ * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
+ * user settings, carrier data on/off won't affect user settings but will bypass the
+ * settings and turns off data internally if set to {@code false}.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ *
+ * @param enabled control enable or disable carrier data.
* @hide
*/
- public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setCarrierDataEnabled(boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.carrierActionSetMeteredApnsEnabled(subId, enabled);
+ service.carrierActionSetMeteredApnsEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled);
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e);
+ Log.e(TAG, "Error calling ITelephony#setCarrierDataEnabled", e);
}
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index eb144f9..aabefe3 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -257,7 +257,7 @@
private final int mProfileId;
- private final boolean mModemCognitive;
+ private final boolean mPersistent;
private final int mMaxConns;
private final int mWaitTime;
private final int mMaxConnsTime;
@@ -290,13 +290,13 @@
}
/**
- * Returns if the APN setting is to be set in modem.
+ * Returns if the APN setting is persistent on the modem.
*
* @return is the APN setting to be set in modem
* @hide
*/
- public boolean getModemCognitive() {
- return mModemCognitive;
+ public boolean isPersistent() {
+ return mPersistent;
}
/**
@@ -616,7 +616,7 @@
this.mCarrierEnabled = builder.mCarrierEnabled;
this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
this.mProfileId = builder.mProfileId;
- this.mModemCognitive = builder.mModemCognitive;
+ this.mPersistent = builder.mModemCognitive;
this.mMaxConns = builder.mMaxConns;
this.mWaitTime = builder.mWaitTime;
this.mMaxConnsTime = builder.mMaxConnsTime;
@@ -740,7 +740,7 @@
apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress,
apn.mMmsProxyPort, apn.mUser, apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask,
apn.mProtocol, apn.mRoamingProtocol, apn.mCarrierEnabled, apn.mNetworkTypeBitmask,
- apn.mProfileId, apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime,
+ apn.mProfileId, apn.mPersistent, apn.mMaxConns, apn.mWaitTime,
apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData, apn.mApnSetId);
}
@@ -947,7 +947,7 @@
sb.append(", ").append(PROTOCOL_INT_MAP.get(mRoamingProtocol));
sb.append(", ").append(mCarrierEnabled);
sb.append(", ").append(mProfileId);
- sb.append(", ").append(mModemCognitive);
+ sb.append(", ").append(mPersistent);
sb.append(", ").append(mMaxConns);
sb.append(", ").append(mWaitTime);
sb.append(", ").append(mMaxConnsTime);
@@ -1029,7 +1029,7 @@
&& Objects.equals(mMmsc, other.mMmsc)
&& Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
&& Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
- && Objects.equals(mProxyPort,other.mProxyPort)
+ && Objects.equals(mProxyPort, other.mProxyPort)
&& Objects.equals(mUser, other.mUser)
&& Objects.equals(mPassword, other.mPassword)
&& Objects.equals(mAuthType, other.mAuthType)
@@ -1038,7 +1038,7 @@
&& Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
&& Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
&& Objects.equals(mProfileId, other.mProfileId)
- && Objects.equals(mModemCognitive, other.mModemCognitive)
+ && Objects.equals(mPersistent, other.mPersistent)
&& Objects.equals(mMaxConns, other.mMaxConns)
&& Objects.equals(mWaitTime, other.mWaitTime)
&& Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
@@ -1080,11 +1080,11 @@
&& Objects.equals(mPassword, other.mPassword)
&& Objects.equals(mAuthType, other.mAuthType)
&& Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
- && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
+ && (isDataRoaming || Objects.equals(mProtocol, other.mProtocol))
&& (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
&& Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
&& Objects.equals(mProfileId, other.mProfileId)
- && Objects.equals(mModemCognitive, other.mModemCognitive)
+ && Objects.equals(mPersistent, other.mPersistent)
&& Objects.equals(mMaxConns, other.mMaxConns)
&& Objects.equals(mWaitTime, other.mWaitTime)
&& Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index e8597b2..da4822c 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -68,17 +68,15 @@
private final int mMtu;
- private final String mMvnoType;
+ private final boolean mPersistent;
- private final String mMvnoMatchData;
+ private final boolean mPreferred;
- private final boolean mModemCognitive;
-
- public DataProfile(int profileId, String apn, String protocol, int authType,
- String userName, String password, int type, int maxConnsTime, int maxConns,
- int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
- int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData,
- boolean modemCognitive) {
+ /** @hide */
+ public DataProfile(int profileId, String apn, String protocol, int authType, String userName,
+ String password, int type, int maxConnsTime, int maxConns, int waitTime,
+ boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
+ int bearerBitmap, int mtu, boolean persistent, boolean preferred) {
this.mProfileId = profileId;
this.mApn = apn;
@@ -100,11 +98,11 @@
this.mRoamingProtocol = roamingProtocol;
this.mBearerBitmap = bearerBitmap;
this.mMtu = mtu;
- this.mMvnoType = mvnoType;
- this.mMvnoMatchData = mvnoMatchData;
- this.mModemCognitive = modemCognitive;
+ this.mPersistent = persistent;
+ this.mPreferred = preferred;
}
+ /** @hide */
public DataProfile(Parcel source) {
mProfileId = source.readInt();
mApn = source.readString();
@@ -121,9 +119,8 @@
mRoamingProtocol = source.readString();
mBearerBitmap = source.readInt();
mMtu = source.readInt();
- mMvnoType = source.readString();
- mMvnoMatchData = source.readString();
- mModemCognitive = source.readBoolean();
+ mPersistent = source.readBoolean();
+ mPreferred = source.readBoolean();
}
/**
@@ -207,23 +204,17 @@
public int getMtu() { return mMtu; }
/**
- * @return The MVNO type: possible values are "imsi", "gid", "spn".
+ * @return {@code true} if modem must persist this data profile.
*/
- public String getMvnoType() { return mMvnoType; }
+ public boolean isPersistent() { return mPersistent; }
/**
- * @return The MVNO match data. For example,
- * SPN: A MOBILE, BEN NL, ...
- * IMSI: 302720x94, 2060188, ...
- * GID: 4E, 33, ...
+ * @return {@code true} if this data profile was used to bring up the last default
+ * (i.e internet) data connection successfully.
*/
- public String getMvnoMatchData() { return mMvnoMatchData; }
+ public boolean isPreferred() { return mPreferred; }
- /**
- * @return True if the data profile was sent to the modem through setDataProfile earlier.
- */
- public boolean isModemCognitive() { return mModemCognitive; }
-
+ /** @hide */
@Override
public int describeContents() {
return 0;
@@ -233,11 +224,11 @@
public String toString() {
return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType
+ "/" + (Build.IS_USER ? "***/***/***" :
- (mApn + "/" + mUserName + "/" + mPassword))
- + "/" + mType + "/" + mMaxConnsTime
- + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/"
- + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/"
- + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive;
+ (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/"
+ + mMaxConnsTime + "/" + mMaxConns + "/"
+ + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmap + "/"
+ + mRoamingProtocol + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/"
+ + mPreferred;
}
@Override
@@ -246,6 +237,7 @@
return (o == this || toString().equals(o.toString()));
}
+ /** @hide */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mProfileId);
@@ -263,11 +255,11 @@
dest.writeString(mRoamingProtocol);
dest.writeInt(mBearerBitmap);
dest.writeInt(mMtu);
- dest.writeString(mMvnoType);
- dest.writeString(mMvnoMatchData);
- dest.writeBoolean(mModemCognitive);
+ dest.writeBoolean(mPersistent);
+ dest.writeBoolean(mPreferred);
}
+ /** @hide */
public static final Parcelable.Creator<DataProfile> CREATOR =
new Parcelable.Creator<DataProfile>() {
@Override
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index d6a08543..bdba8c8 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -33,7 +33,7 @@
* A parcelable class that wraps and retrieves the information of number, service category(s) and
* country code for a specific emergency number.
*/
-public final class EmergencyNumber implements Parcelable {
+public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNumber> {
private static final String LOG_TAG = "EmergencyNumber";
@@ -235,20 +235,22 @@
}
/**
- * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of
- * the emergency number.
+ * Returns the bitmask of emergency service categories of the emergency number.
*
- * @return bitmask of the emergency service categories {@link EmergencyServiceCategories}
+ * @return bitmask of the emergency service categories
*/
public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
return mEmergencyServiceCategoryBitmask;
}
/**
- * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency
- * number.
+ * Returns the emergency service categories of the emergency number.
*
- * @return a list of the emergency service categories {@link EmergencyServiceCategories}
+ * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
+ * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in
+ * all categories.
+ *
+ * @return a list of the emergency service categories
*/
public List<Integer> getEmergencyServiceCategories() {
List<Integer> categories = new ArrayList<>();
@@ -276,34 +278,37 @@
}
/**
- * Checks if the emergency number is in the specified emergency service category(s)
- * {@link EmergencyServiceCategories}.
+ * Checks if the emergency number is in the supplied emergency service category(s).
+ *
+ * @param categories - the supplied emergency service categories
*
* @return {@code true} if the emergency number is in the specified emergency service
- * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise.
- *
- * @param categories - emergency service categories {@link EmergencyServiceCategories}
+ * category(s) or if its emergency service category is
+ * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
*/
public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
return serviceUnspecified();
}
+ if (serviceUnspecified()) {
+ return true;
+ }
return (mEmergencyServiceCategoryBitmask & categories) == categories;
}
/**
- * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number.
+ * Returns the bitmask of the sources of the emergency number.
*
- * @return bitmask of the emergency number sources {@link EmergencyNumberSources}
+ * @return bitmask of the emergency number sources
*/
public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
return mEmergencyNumberSourceBitmask;
}
/**
- * Returns a list of {@link EmergencyNumberSources} of the emergency number.
+ * Returns a list of sources of the emergency number.
*
- * @return a list of {@link EmergencyNumberSources}
+ * @return a list of emergency number sources
*/
public List<Integer> getEmergencyNumberSources() {
List<Integer> sources = new ArrayList<>();
@@ -316,13 +321,12 @@
}
/**
- * Checks if the emergency number is from the specified emergency number source(s)
- * {@link EmergencyNumberSources}.
+ * Checks if the emergency number is from the specified emergency number source(s).
*
* @return {@code true} if the emergency number is from the specified emergency number
- * source(s) {@link EmergencyNumberSources}; {@code false} otherwise.
+ * source(s); {@code false} otherwise.
*
- * @param sources - {@link EmergencyNumberSources}
+ * @param sources - the supplied emergency number sources
*/
public boolean isFromSources(@EmergencyNumberSources int sources) {
return (mEmergencyNumberSourceBitmask & sources) == sources;
@@ -359,6 +363,62 @@
return (o == this || toString().equals(o.toString()));
}
+ /**
+ * Calculate the score for display priority.
+ *
+ * A higher display priority score means the emergency number has a higher display priority.
+ * The score is higher if the source is defined for a higher display priority.
+ *
+ * The priority of sources are defined as follows:
+ * EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
+ * EMERGENCY_NUMBER_SOURCE_SIM >
+ * EMERGENCY_NUMBER_SOURCE_DEFAULT >
+ * EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+ *
+ */
+ private int getDisplayPriorityScore() {
+ int score = 0;
+ if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) {
+ score += 1 << 4;
+ }
+ if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
+ score += 1 << 3;
+ }
+ // TODO add a score if the number comes from Google's emergency number database
+ if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
+ score += 1 << 1;
+ }
+ if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) {
+ score += 1 << 0;
+ }
+ return score;
+ }
+
+ /**
+ * Compare the display priority for this emergency number and the supplied emergency number.
+ *
+ * @param emergencyNumber the supplied emergency number
+ * @return a negative value if the supplied emergency number has a lower display priority;
+ * a positive value if the supplied emergency number has a higher display priority;
+ * 0 if both have equal display priority.
+ */
+ @Override
+ public int compareTo(EmergencyNumber emergencyNumber) {
+ if (this.getDisplayPriorityScore()
+ > emergencyNumber.getDisplayPriorityScore()) {
+ return -1;
+ } else if (this.getDisplayPriorityScore()
+ < emergencyNumber.getDisplayPriorityScore()) {
+ return 1;
+ } else {
+ /**
+ * TODO if both numbers have the same display priority score, the number matches the
+ * Google's emergency number database has a higher display priority.
+ */
+ return 0;
+ }
+ }
+
public static final Parcelable.Creator<EmergencyNumber> CREATOR =
new Parcelable.Creator<EmergencyNumber>() {
@Override
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index 8d18ae8..f2d0cbf 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -16,22 +16,19 @@
package android.telephony.ims;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.Log;
import android.telephony.Rlog;
-/*
- * This file contains all the api's through which
- * information received in Dialog Event Package can be
- * queried
- */
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
- * Parcelable object to handle MultiEndpoint Dialog Information
+ * Parcelable object to handle MultiEndpoint Dialog Event Package Information.
* @hide
*/
@SystemApi
@@ -40,8 +37,39 @@
private static final String TAG = "ImsExternalCallState";
// Dialog States
+ /**
+ * The external call is in the confirmed dialog state.
+ */
public static final int CALL_STATE_CONFIRMED = 1;
+ /**
+ * The external call is in the terminated dialog state.
+ */
public static final int CALL_STATE_TERMINATED = 2;
+
+ /**@hide*/
+ @IntDef(flag = true,
+ value = {
+ CALL_STATE_CONFIRMED,
+ CALL_STATE_TERMINATED
+ },
+ prefix = "CALL_STATE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ExternalCallState {}
+
+ /**@hide*/
+ @IntDef(flag = true,
+ value = {
+ ImsCallProfile.CALL_TYPE_VOICE,
+ ImsCallProfile.CALL_TYPE_VT_TX,
+ ImsCallProfile.CALL_TYPE_VT_RX,
+ ImsCallProfile.CALL_TYPE_VT
+ },
+ prefix = "CALL_TYPE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ExternalCallType {}
+
+
+
// Dialog Id
private int mCallId;
// Number
@@ -58,10 +86,9 @@
public ImsExternalCallState() {
}
- /** @hide */
- @UnsupportedAppUsage
- public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState,
- int callType, boolean isCallheld) {
+ /**@hide*/
+ public ImsExternalCallState(int callId, Uri address, boolean isPullable,
+ @ExternalCallState int callState, int callType, boolean isCallheld) {
mCallId = callId;
mAddress = address;
mIsPullable = isPullable;
@@ -71,9 +98,10 @@
Rlog.d(TAG, "ImsExternalCallState = " + this);
}
- /** @hide */
+ /**@hide*/
public ImsExternalCallState(int callId, Uri address, Uri localAddress,
- boolean isPullable, int callState, int callType, boolean isCallheld) {
+ boolean isPullable, @ExternalCallState int callState, int callType,
+ boolean isCallheld) {
mCallId = callId;
mAddress = address;
mLocalAddress = localAddress;
@@ -84,6 +112,31 @@
Rlog.d(TAG, "ImsExternalCallState = " + this);
}
+ /**
+ * Create a new ImsExternalCallState instance to contain Multiendpoint Dialog information.
+ * @param callId The unique ID of the call, which will be used to identify this external
+ * connection.
+ * @param address A {@link Uri} containing the remote address of this external connection.
+ * @param localAddress A {@link Uri} containing the local address information.
+ * @param isPullable A flag determining if this external connection can be pulled to the current
+ * device.
+ * @param callState The state of the external call.
+ * @param callType The type of external call.
+ * @param isCallheld A flag determining if the external connection is currently held.
+ */
+ public ImsExternalCallState(String callId, Uri address, Uri localAddress,
+ boolean isPullable, @ExternalCallState int callState, @ExternalCallType int callType,
+ boolean isCallheld) {
+ mCallId = getIdForString(callId);
+ mAddress = address;
+ mLocalAddress = localAddress;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
/** @hide */
public ImsExternalCallState(Parcel in) {
mCallId = in.readInt();
@@ -135,7 +188,9 @@
return mAddress;
}
- /** @hide */
+ /**
+ * @return A {@link Uri} containing the local address from the Multiendpoint Dialog Information.
+ */
public Uri getLocalAddress() {
return mLocalAddress;
}
@@ -144,11 +199,11 @@
return mIsPullable;
}
- public int getCallState() {
+ public @ExternalCallState int getCallState() {
return mCallState;
}
- public int getCallType() {
+ public @ExternalCallType int getCallType() {
return mCallType;
}
@@ -166,4 +221,15 @@
", mCallType = " + mCallType +
", mIsHeld = " + mIsHeld + "}";
}
+
+ private int getIdForString(String idString) {
+ try {
+ return Integer.parseInt(idString);
+ } catch (NumberFormatException e) {
+ // In the case that there are alphanumeric characters, we will create a hash of the
+ // String value as a backup.
+ // TODO: Modify call IDs to use Strings as keys instead of integers in telephony/telecom
+ return idString.hashCode();
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
new file mode 100644
index 0000000..c9cf473
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -0,0 +1,760 @@
+/*
+ * 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.telephony.ims;
+
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
+ * subscription.
+ *
+ * Allows a user to query the IMS MmTel feature information for a subscription, register for
+ * registration and MmTel capability status callbacks, as well as query/modify user settings for the
+ * associated subscription.
+ *
+ * @see #createForSubscriptionId(Context, int)
+ * @hide
+ */
+public class ImsMmTelManager {
+
+ private static final String TAG = "ImsMmTelManager";
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "WIFI_MODE_", value = {
+ WIFI_MODE_WIFI_ONLY,
+ WIFI_MODE_CELLULAR_PREFERRED,
+ WIFI_MODE_WIFI_PREFERRED
+ })
+ public @interface WiFiCallingMode {}
+
+ /**
+ * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
+ * registration if signal quality degrades.
+ * @hide
+ */
+ @SystemApi
+ public static final int WIFI_MODE_WIFI_ONLY = 0;
+
+ /**
+ * Prefer registering for IMS over LTE if LTE signal quality is high enough.
+ * @hide
+ */
+ @SystemApi
+ public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
+
+ /**
+ * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
+ * @hide
+ */
+ @SystemApi
+ public static final int WIFI_MODE_WIFI_PREFERRED = 2;
+
+ /**
+ * Callback class for receiving Registration callback events.
+ * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+ * @see #removeImsRegistrationCallback(RegistrationCallback)
+ */
+ public static class RegistrationCallback {
+
+ private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+ private final RegistrationCallback mLocalCallback;
+ private Executor mExecutor;
+
+ RegistrationBinder(RegistrationCallback localCallback) {
+ mLocalCallback = localCallback;
+ }
+
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+ }
+
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onDeregistered(info)));
+ }
+
+ @Override
+ public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+ }
+
+ @Override
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+ /**
+ * Notifies the framework when the IMS Provider is registered to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ */
+ public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ */
+ public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public void onDeregistered(ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type,
+ * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ *
+ * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
+ * failed
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ public void onTechnologyChangeFailed(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+ }
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+ * allocated to a user for their own usage. A user's phone number is typically one of the
+ * associated URIs.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ * @hide
+ */
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ }
+
+ /**@hide*/
+ public final IImsRegistrationCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ //Only exposed as public for compatibility with deprecated ImsManager APIs.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ /**
+ * Receives IMS capability status updates from the ImsService.
+ *
+ * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+ * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ */
+ public static class CapabilityCallback {
+
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+ private final CapabilityCallback mLocalCallback;
+ private Executor mExecutor;
+
+ CapabilityBinder(CapabilityCallback c) {
+ mLocalCallback = c;
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
+ new MmTelFeature.MmTelCapabilities(config))));
+ }
+
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ // This is not used for public interfaces.
+ }
+
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsFeature.ImsCapabilityError int reason) {
+ // This is not used for public interfaces
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+ /**
+ * The status of the feature's capabilities has changed to either available or unavailable.
+ * If unavailable, the feature is not able to support the unavailable capability at this
+ * time.
+ *
+ * @param capabilities The new availability of the capabilities.
+ */
+ public void onCapabilitiesStatusChanged(
+ MmTelFeature.MmTelCapabilities capabilities) {
+ }
+
+ /**@hide*/
+ public final IImsCapabilityCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ // Only exposed as public method for compatibility with deprecated ImsManager APIs.
+ // TODO: clean up dependencies and change back to private visibility.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private Context mContext;
+ private int mSubId;
+
+ /**
+ * Create an instance of ImsManager for the subscription id specified.
+ *
+ * @param context
+ * @param subId The ID of the subscription that this ImsManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ * @throws IllegalArgumentException if the subscription is invalid or
+ * the subscription ID is not an active subscription.
+ */
+ public static ImsMmTelManager createForSubscriptionId(Context context, int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)
+ || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ImsMmTelManager(context, subId);
+ }
+
+ private ImsMmTelManager(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Registers a {@link RegistrationCallback} with the system, which will provide registration
+ * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
+ * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+ * after a subscription is removed.
+ * @param executor The executor the callback events should be run on.
+ * @param c The {@link RegistrationCallback} to be added.
+ * @see #removeImsRegistrationCallback(RegistrationCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+ @NonNull RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ try {
+ getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning
+ * up to avoid memory leaks or when the subscription is removed.
+ * @param c The {@link RegistrationCallback} to be removed.
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ try {
+ getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability
+ * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
+ * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+ * subscription changed events and call
+ * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
+ * is removed.
+ * @param executor The executor the callback events should be run on.
+ * @param c The MmTel {@link CapabilityCallback} to be registered.
+ * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+ @NonNull CapabilityCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ try {
+ getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
+ * up to avoid memory leaks.
+ * @param c The MmTel {@link CapabilityCallback} to be removed.
+ * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ try {
+ getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the user's setting for whether or not to use MmTel capabilities over IMS,
+ * such as voice and video, depending on carrier configuration for the current subscription.
+ * @see #setAdvancedCallingSetting(boolean)
+ * @return true if the user’s setting for advanced calling is enabled and false otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isAdvancedCallingSettingEnabled() {
+ try {
+ return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+ * enable MmTel IMS features, such as voice and video calling, depending on the carrier
+ * configuration for the current subscription. Modifying this value may also trigger an IMS
+ * registration or deregistration, depending on the new value.
+ * @see #isAdvancedCallingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setAdvancedCallingSetting(boolean isEnabled) {
+ try {
+ getITelephony().setAdvancedCallingSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the IMS MmTel capability for a given registration technology. This does not
+ * necessarily mean that we are registered and the capability is available, but rather the
+ * subscription is capable of this service over IMS.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+ * @see #isAvailable(int, int)
+ *
+ * @param imsRegTech The IMS registration technology, can be one of the following:
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * @param capability The IMS MmTel capability to query, can be one of the following:
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
+ * otherwise.
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ try {
+ return getITelephony().isCapable(mSubId, capability, imsRegTech,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the availability of an IMS MmTel capability for a given registration technology. If
+ * a capability is available, IMS is registered and the service is currently available over IMS.
+ *
+ * @see #isCapable(int, int)
+ *
+ * @param imsRegTech The IMS registration technology, can be one of the following:
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * @param capability The IMS MmTel capability to query, can be one of the following:
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ * @return {@code true} if the MmTel IMS capability is available for this subscription, false
+ * otherwise.
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ try {
+ return getITelephony().isAvailable(mSubId, capability, imsRegTech,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * The user's setting for whether or not they have enabled the "Video Calling" setting.
+ * @return true if the user’s “Video Calling” setting is currently enabled.
+ * @see #setVtSetting(boolean)
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isVtSettingEnabled() {
+ try {
+ return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+ * @see #isVtSettingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVtSetting(boolean isEnabled) {
+ try {
+ getITelephony().setVtSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+ * @see #setVoWiFiSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isVoWiFiSettingEnabled() {
+ try {
+ return getITelephony().isVoWiFiSettingEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sets the user's setting for whether or not Voice over WiFi is enabled.
+ * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
+ * @see #isVoWiFiSettingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiSetting(boolean isEnabled) {
+ try {
+ getITelephony().setVoWiFiSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
+ * if disabled.
+ * @see #setVoWiFiRoamingSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isVoWiFiRoamingSettingEnabled() {
+ try {
+ return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for Voice over WiFi while roaming.
+ * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
+ * false otherwise.
+ * @see #isVoWiFiRoamingSettingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiRoamingSetting(boolean isEnabled) {
+ try {
+ getITelephony().setVoWiFiRoamingSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
+ * Typically used during the Voice over WiFi registration process for some carriers.
+ *
+ * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
+ * otherwise.
+ * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #setVoWiFiSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+ try {
+ getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return The Voice over WiFi Mode preference set by the user, which can be one of the
+ * following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #setVoWiFiSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @WiFiCallingMode int getVoWiFiModeSetting() {
+ try {
+ return getITelephony().getVoWiFiModeSetting(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi calling mode.
+ * @param mode The user's preference for the technology to register for IMS over, can be one of
+ * the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #getVoWiFiModeSetting()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+ try {
+ getITelephony().setVoWiFiModeSetting(mSubId, mode);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
+ * another network.
+ *
+ * @return The user's preference for the technology to register for IMS over when roaming on
+ * another network, can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #setVoWiFiRoamingSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+ try {
+ return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi mode while the device is roaming on another
+ * network.
+ *
+ * @param mode The user's preference for the technology to register for IMS over when roaming on
+ * another network, can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #getVoWiFiRoamingModeSetting()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+ try {
+ getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for RTT capability of this device.
+ * @param isEnabled if true RTT will be enabled during calls.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRttCapabilitySetting(boolean isEnabled) {
+ try {
+ getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if TTY over VoLTE is supported
+ * @see android.telecom.TelecomManager#getCurrentTtyMode
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ boolean isTtyOverVolteEnabled() {
+ try {
+ return getITelephony().isTtyOverVolteEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ private static SubscriptionManager getSubscriptionManager(Context context) {
+ SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+ if (manager == null) {
+ throw new RuntimeException("Could not find SubscriptionManager.");
+ }
+ return manager;
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ if (binder == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+ return binder;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 4f37caa..749b191 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -23,7 +23,7 @@
import android.telephony.ims.ImsReasonInfo;
/**
- * See ImsRegistrationImplBase.Callback for more information.
+ * See {@link ImsManager#RegistrationCallback} for more information.
*
* {@hide}
*/
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b77881e..7f69f43 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -167,59 +167,6 @@
*/
public static final int CAPABILITY_SUCCESS = 0;
-
- /**
- * The framework implements this callback in order to register for Feature Capability status
- * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
- * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
- * callbacks when the ImsService can not change the capability as requested, via
- * {@link #onChangeCapabilityConfigurationError}.
- *
- * @hide
- */
- public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
- @Override
- public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
- onCapabilitiesStatusChanged(new Capabilities(config));
- }
-
- /**
- * Returns the result of a query for the capability configuration of a requested capability.
- *
- * @param capability The capability that was requested.
- * @param radioTech The IMS radio technology associated with the capability.
- * @param isEnabled true if the capability is enabled, false otherwise.
- */
- @Override
- public void onQueryCapabilityConfiguration(int capability, int radioTech,
- boolean isEnabled) {
-
- }
-
- /**
- * Called when a change to the capability configuration has returned an error.
- *
- * @param capability The capability that was requested to be changed.
- * @param radioTech The IMS radio technology associated with the capability.
- * @param reason error associated with the failure to change configuration.
- */
- @Override
- public void onChangeCapabilityConfigurationError(int capability, int radioTech,
- @ImsCapabilityError int reason) {
- }
-
- /**
- * The status of the feature's capabilities has changed to either available or unavailable.
- * If unavailable, the feature is not able to support the unavailable capability at this
- * time.
- *
- * @param config The new availability of the capabilities.
- */
- public void onCapabilitiesStatusChanged(Capabilities config) {
- }
- }
-
/**
* Used by the ImsFeature to call back to the CapabilityCallback that the framework has
* provided.
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7681aef..96995943 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -17,6 +17,8 @@
package android.telephony.ims.feature;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Message;
@@ -222,21 +224,31 @@
* This MmTelFeature can then return the status of each of these capabilities (enabled or not)
* by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
* status can also be queried using {@link #queryCapabilityStatus()}.
+ * @see #isCapable(int)
*/
public static class MmTelCapabilities extends Capabilities {
/**
- * @hide
+ * Create a new empty {@link MmTelCapabilities} instance.
+ * @see #addCapabilities(int)
+ * @see #removeCapabilities(int)
*/
@VisibleForTesting
public MmTelCapabilities() {
super();
}
+ /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/
+ @Deprecated
public MmTelCapabilities(Capabilities c) {
mCapabilities = c.mCapabilities;
}
+ /**
+ * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities that are supported for MmTel in the form of a
+ * bitfield.
+ */
public MmTelCapabilities(int capabilities) {
mCapabilities = capabilities;
}
@@ -406,7 +418,10 @@
* support the capability that is enabled. A capability that is disabled by the framework (via
* {@link #changeEnabledCapabilities}) should also show the status as disabled.
*/
- public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+ public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
+ if (c == null) {
+ throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
+ }
super.notifyCapabilitiesStatusChanged(c);
}
@@ -414,7 +429,12 @@
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
*/
- public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+ public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
+ @NonNull Bundle extras) {
+ if (c == null || extras == null) {
+ throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+ + "null.");
+ }
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
@@ -434,7 +454,12 @@
* This can be null if no call information is available for the rejected call.
* @param reason The {@link ImsReasonInfo} call rejection reason.
*/
- public final void notifyRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
+ public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
+ @NonNull ImsReasonInfo reason) {
+ if (callProfile == null || reason == null) {
+ throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+ + "null.");
+ }
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
@@ -508,8 +533,8 @@
* the framework.
*/
@Override
- public void changeEnabledCapabilities(CapabilityChangeRequest request,
- CapabilityCallbackProxy c) {
+ public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+ @NonNull CapabilityCallbackProxy c) {
// Base implementation, no-op
}
@@ -531,7 +556,7 @@
* {@link ImsCallProfile#CALL_TYPE_VS_RX}
* @return a {@link ImsCallProfile} object
*/
- public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+ public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
// Base Implementation - Should be overridden
return null;
}
@@ -552,7 +577,7 @@
*
* @param profile a call profile to make the call
*/
- public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+ public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
// Base Implementation - Should be overridden
return null;
}
@@ -569,7 +594,7 @@
* @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
* call will be placed over IMS or via CSFB.
*/
- public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+ public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
return PROCESS_CALL_IMS;
}
@@ -602,7 +627,7 @@
* @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
* configuration.
*/
- public ImsUtImplBase getUt() {
+ public @NonNull ImsUtImplBase getUt() {
// Base Implementation - Should be overridden
return new ImsUtImplBase();
}
@@ -611,7 +636,7 @@
* @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
* calls that support it.
*/
- public ImsEcbmImplBase getEcbm() {
+ public @NonNull ImsEcbmImplBase getEcbm() {
// Base Implementation - Should be overridden
return new ImsEcbmImplBase();
}
@@ -620,7 +645,7 @@
* @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
* package processing for multi-endpoint.
*/
- public ImsMultiEndpointImplBase getMultiEndpoint() {
+ public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
// Base Implementation - Should be overridden
return new ImsMultiEndpointImplBase();
}
@@ -646,7 +671,7 @@
* }
* }
*/
- public void setUiTtyMode(int mode, Message onCompleteMessage) {
+ public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
// Base Implementation - Should be overridden
}
@@ -680,7 +705,7 @@
* @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
* Provider.
*/
- public ImsSmsImplBase getSmsImplementation() {
+ public @NonNull ImsSmsImplBase getSmsImplementation() {
return new ImsSmsImplBase();
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index cecf2e2..a08e031 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -76,64 +76,6 @@
private static final int REGISTRATION_STATE_REGISTERING = 1;
private static final int REGISTRATION_STATE_REGISTERED = 2;
- /**
- * Callback class for receiving Registration callback events.
- * @hide
- */
- public static class Callback extends IImsRegistrationCallback.Stub {
- /**
- * Notifies the framework when the IMS Provider is connected to the IMS network.
- *
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationTech}.
- */
- @Override
- public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
- }
-
- /**
- * Notifies the framework when the IMS Provider is trying to connect the IMS network.
- *
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationTech}.
- */
- @Override
- public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
- }
-
- /**
- * Notifies the framework when the IMS Provider is disconnected from the IMS network.
- *
- * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
- */
- @Override
- public void onDeregistered(ImsReasonInfo info) {
- }
-
- /**
- * A failure has occurred when trying to handover registration to another technology type,
- * defined in {@link ImsRegistrationTech}
- *
- * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
- * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
- */
- @Override
- public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
- ImsReasonInfo info) {
- }
-
- /**
- * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
- * it changes.
- * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
- * subscription.
- */
- @Override
- public void onSubscriberAssociatedUriChanged(Uri[] uris) {
-
- }
- }
-
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@Override
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 86cb1b7..b0c875e 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -100,6 +100,7 @@
public static final int EVENT_DATA_RECONNECT = BASE + 47;
public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
+ public static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = BASE + 50;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 38a1bc7..9e42f12 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -53,5 +53,6 @@
void onUserMobileDataStateChanged(in boolean enabled);
void onPhoneCapabilityChanged(in PhoneCapability capability);
void onPreferredDataSubIdChanged(in int subId);
+ void onRadioPowerStateChanged(in int state);
}
diff --git a/telephony/java/com/android/internal/telephony/IRcs.aidl b/telephony/java/com/android/internal/telephony/IRcs.aidl
new file mode 100644
index 0000000..ede8695
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IRcs.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+interface IRcs {
+ void deleteThread(int threadId);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 32eb12b..dc23358 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,10 +38,12 @@
import android.telephony.SignalStrength;
import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
@@ -280,7 +282,7 @@
/**
* Returns the neighboring cell information of the device.
*/
- List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg, int targetSdk);
+ List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
int getCallState();
@@ -1057,11 +1059,6 @@
*/
boolean isWifiCallingAvailable(int subId);
- /**
- * Returns the Status of VoLTE for the subscription ID specified.
- */
- boolean isVolteAvailable(int subId);
-
/**
* Returns the Status of VT (video telephony) for the subscription ID specified.
*/
@@ -1499,4 +1496,123 @@
* Set the default SMS app to a given package on a given user.
*/
void setDefaultSmsApp(int userId, String packageName);
+
+ /**
+ * Return the modem radio power state for slot index.
+ *
+ */
+ int getRadioPowerState(int slotIndex, String callingPackage);
+
+ // IMS specific AIDL commands, see ImsMmTelManager.java
+
+ /**
+ * Adds an IMS registration status callback for the subscription id specified.
+ */
+ oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ String callingPackage);
+ /**
+ * Removes an existing IMS registration status callback for the subscription specified.
+ */
+ oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ String callingPackage);
+
+ /**
+ * Adds an IMS MmTel capabilities callback for the subscription specified.
+ */
+ oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ String callingPackage);
+
+ /**
+ * Removes an existing IMS MmTel capabilities callback for the subscription specified.
+ */
+ oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ String callingPackage);
+
+ /**
+ * return true if the IMS MmTel capability for the given registration tech is capable.
+ */
+ boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+
+ /**
+ * return true if the IMS MmTel capability for the given registration tech is available.
+ */
+ boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+
+ /**
+ * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
+ */
+ boolean isAdvancedCallingSettingEnabled(int subId);
+
+ /**
+ * Modify the user's setting for whether or not 4G LTE is enabled.
+ */
+ void setAdvancedCallingSetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if the user's setting for VT is enabled for the subscription.
+ */
+ boolean isVtSettingEnabled(int subId, String callingPackage);
+
+ /**
+ * Modify the user's setting for whether or not VT is available for the subscrption specified.
+ */
+ void setVtSetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if the user's setting for whether or not Voice over WiFi is currently enabled.
+ */
+ boolean isVoWiFiSettingEnabled(int subId);
+
+ /**
+ * sets the user's setting for Voice over WiFi enabled state.
+ */
+ void setVoWiFiSetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if the user's setting for Voice over WiFi while roaming is enabled.
+ */
+ boolean isVoWiFiRoamingSettingEnabled(int subId);
+
+ /**
+ * Sets the user's preference for whether or not Voice over WiFi is enabled for the current
+ * subscription while roaming.
+ */
+ void setVoWiFiRoamingSetting(int subId, boolean isEnabled);
+
+ /**
+ * Set the Voice over WiFi enabled state, but do not persist the setting.
+ */
+ void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode);
+
+ /**
+ * return the Voice over WiFi mode preference set by the user for the subscription specified.
+ */
+ int getVoWiFiModeSetting(int subId);
+
+ /**
+ * sets the user's preference for the Voice over WiFi mode for the subscription specified.
+ */
+ void setVoWiFiModeSetting(int subId, int mode);
+
+ /**
+ * return the Voice over WiFi mode preference set by the user for the subscription specified
+ * while roaming.
+ */
+ int getVoWiFiRoamingModeSetting(int subId);
+
+ /**
+ * sets the user's preference for the Voice over WiFi mode for the subscription specified
+ * while roaming.
+ */
+ void setVoWiFiRoamingModeSetting(int subId, int mode);
+
+ /**
+ * Modify the user's setting for whether or not RTT is enabled for the subscrption specified.
+ */
+ void setRttCapabilitySetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if TTY over VoLTE is enabled for the subscription specified.
+ */
+ boolean isTtyOverVolteEnabled(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index c03065c..0baf860 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -80,4 +80,5 @@
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
void notifyPreferredDataSubIdChanged(int preferredSubId);
+ void notifyRadioPowerStateChanged(in int state);
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 9730ebc..eda8e77 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -44,10 +45,6 @@
private static final boolean DBG = false;
- // When set to true this flag will treat all apps that fail the device identifier check as
- // though they are targeting pre-Q and return dummy data instead of throwing a SecurityException
- private static final boolean RELAX_DEVICE_IDENTIFIER_CHECK = true;
-
private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () ->
ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
@@ -280,23 +277,29 @@
*/
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
- // if the device identifier check is relaxed then just return false to return dummy data to
- // the caller instead of throwing a SecurityException for apps targeting Q+.
- if (RELAX_DEVICE_IDENTIFIER_CHECK) {
- Log.wtf(LOG_TAG,
- "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
- return false;
+ Log.wtf(LOG_TAG,
+ "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
+ // if the device identifier check is relaxed then revert to the READ_PHONE_STATE permission
+ // check that was previously required to access device identifiers.
+ boolean relaxDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 0;
+ if (relaxDeviceIdentifierCheck) {
+ return checkReadPhoneState(context, subId, pid, uid, callingPackage, message);
} else {
+ boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
if (callingPackage != null) {
try {
- // if the target SDK is pre-Q then check if the calling package would have
- // previously had access to device identifiers.
+ // if the target SDK is pre-Q or the target Q behavior is disabled then check if
+ // the calling package would have previously had access to device identifiers.
ApplicationInfo callingPackageInfo =
context.getPackageManager().getApplicationInfo(
callingPackage, 0);
- if (callingPackageInfo != null
- && callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
- if (context.checkPermission(android.Manifest.permission.READ_PHONE_STATE,
+ if (callingPackageInfo != null && (
+ callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q
+ || targetQBehaviorDisabled)) {
+ if (context.checkPermission(
+ android.Manifest.permission.READ_PHONE_STATE,
pid,
uid) == PackageManager.PERMISSION_GRANTED) {
return false;
@@ -312,8 +315,8 @@
// default to throwing the SecurityException.
}
}
- throw new SecurityException(message + ": The user " + uid + " does not have the "
- + "READ_PRIVILEGED_PHONE_STATE permission to access the device identifiers");
+ throw new SecurityException(message + ": The user " + uid
+ + " does not meet the requirements to access device identifiers.");
}
}
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index 3bd3d68..2b968ae 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -29,6 +29,7 @@
method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+ method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
method public void setUpdateAvailable(java.lang.String, boolean);
method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 9d260eb..fa5b896 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -774,6 +774,12 @@
/** @hide */
@Override
+ public int getDisplayId() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
public void updateDisplay(int displayId) {
throw new UnsupportedOperationException();
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 976848c..eed8ae7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -92,7 +92,9 @@
"com.google.android.wearable.action.GOOGLE";
private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
- private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
+ private static final int BEFORE_FORCE_STOP_SLEEP_TIMEOUT = 1000; // 1s before force stopping
+ private static final int BEFORE_KILL_APP_SLEEP_TIMEOUT = 1000; // 1s before killing
+ private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 3000; // 3s between launching apps
private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
private static final String LAUNCH_FILE = "applaunch.txt";
@@ -327,7 +329,14 @@
}
}
if(mForceStopApp) {
- closeApp(launch.getApp());
+ sleep(BEFORE_FORCE_STOP_SLEEP_TIMEOUT);
+ forceStopApp(launch.getApp());
+ sleep(BEFORE_KILL_APP_SLEEP_TIMEOUT);
+ // Close again for good measure (just in case).
+ forceStopApp(launch.getApp());
+ // Kill the backgrounded process in the case forceStopApp only sent it to
+ // background.
+ killBackgroundApp(launch.getApp());
} else {
startHomeIntent();
}
@@ -638,7 +647,7 @@
// Kill all the apps
for (String appName : mNameToIntent.keySet()) {
Log.w(TAG, String.format("killing %s", appName));
- closeApp(appName);
+ forceStopApp(appName);
}
// Drop all the cache.
assertNotNull("Issue in dropping the cache",
@@ -646,7 +655,7 @@
.executeShellCommand(DROP_CACHE_SCRIPT));
}
- private void closeApp(String appName) {
+ private void forceStopApp(String appName) {
Intent startIntent = mNameToIntent.get(appName);
if (startIntent != null) {
String packageName = startIntent.getComponent().getPackageName();
@@ -658,6 +667,18 @@
}
}
+ private void killBackgroundApp(String appName) {
+ Intent startIntent = mNameToIntent.get(appName);
+ if (startIntent != null) {
+ String packageName = startIntent.getComponent().getPackageName();
+ try {
+ mAm.killBackgroundProcesses(packageName, UserHandle.USER_CURRENT);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error closing app", e);
+ }
+ }
+ }
+
private void sleep(int time) {
try {
Thread.sleep(time);
diff --git a/tests/Internal/res/xml/livewallpaper.xml b/tests/Internal/res/xml/livewallpaper.xml
index 6b5e84e..36e7e41 100644
--- a/tests/Internal/res/xml/livewallpaper.xml
+++ b/tests/Internal/res/xml/livewallpaper.xml
@@ -16,4 +16,5 @@
-->
<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
- android:supportsAmbientMode="true"/>
\ No newline at end of file
+ android:settingsSliceUri="content://com.android.internal.tests/slice"
+ android:supportsAmbientMode="true"/>
diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java
index 98045ae..7f06f2c 100644
--- a/tests/Internal/src/android/app/WallpaperInfoTest.java
+++ b/tests/Internal/src/android/app/WallpaperInfoTest.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Parcel;
import android.service.wallpaper.WallpaperService;
import android.support.test.InstrumentationRegistry;
@@ -64,5 +65,31 @@
fromParcel.supportsAmbientMode());
parcel.recycle();
}
+
+ @Test
+ public void testGetSettingsSliceUri() throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+
+ Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ intent.setPackage("com.android.internal.tests");
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+ assertEquals(1, result.size());
+ ResolveInfo info = result.get(0);
+ WallpaperInfo wallpaperInfo = new WallpaperInfo(context, info);
+
+ // This expected Uri must be the same as that in livewallpaper.xml
+ Uri expectedUri = Uri.parse("content://com.android.internal.tests/slice");
+ Uri settingsUri = wallpaperInfo.getSettingsSliceUri();
+ assertEquals("The loaded URI should equal to the string in livewallpaper.xml",
+ 0, expectedUri.compareTo(settingsUri));
+ Parcel parcel = Parcel.obtain();
+ wallpaperInfo.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+ WallpaperInfo fromParcel = WallpaperInfo.CREATOR.createFromParcel(parcel);
+ assertEquals("settingsSliceUri should be restorable from parcelable",
+ 0, expectedUri.compareTo(fromParcel.getSettingsSliceUri()));
+ parcel.recycle();
+ }
}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
index 6500428..18cdf96 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -34,7 +34,7 @@
// Do an explicit GC in the system server process as part of the test
// case to reduce GC-related sources of noise.
// SIGUSR1 = 10 is the magic signal to trigger the GC.
- int pid = mDevice.getPidForProcess("system_server");
+ int pid = mDevice.getProcessPid("system_server");
mDevice.executeShellCommand("kill -10 " + pid);
// Invoke the Device Cujs instrumentation to run the cujs.
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
index 03503ce..26146ca 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
@@ -19,9 +19,6 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import java.util.InputMismatchException;
-import java.util.Scanner;
-
/**
* Wrapper around ITestDevice exposing useful device functions.
*/
@@ -58,29 +55,15 @@
/**
* Returns the pid for the process with the given name.
*/
- public int getPidForProcess(String name) throws TestException {
- String psout = executeShellCommand("ps -A -o PID,CMD");
- Scanner sc = new Scanner(psout);
+ public int getProcessPid(String name) throws TestException {
try {
- // ps output is of the form:
- // PID CMD
- // 1 init
- // 2 kthreadd
- // ...
- // 9693 ps
- sc.nextLine();
- while (sc.hasNextLine()) {
- int pid = sc.nextInt();
- String cmd = sc.next();
-
- if (name.equals(cmd)) {
- return pid;
- }
+ String pid = mDevice.getProcessPid(name);
+ if (pid == null) {
+ throw new TestException("failed to get pid for " + name);
}
- } catch (InputMismatchException e) {
- throw new TestException("unexpected ps output format: " + psout, e);
+ return Integer.parseInt(pid);
+ } catch (DeviceNotAvailableException e) {
+ throw new TestException(e);
}
-
- throw new TestException("failed to get pid for process " + name);
}
}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
index b408a81..b46e642 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -79,7 +79,7 @@
// adb root access is required to get showmap
mDevice.enableAdbRoot();
- int pid = mDevice.getPidForProcess("system_server");
+ int pid = mDevice.getProcessPid("system_server");
// Read showmap for system server and add it as a test log
String showmap = mDevice.executeShellCommand("showmap " + pid);
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 03a617c..6174c6c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -219,7 +219,7 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
- any(NetworkCapabilities.class), any(LinkProperties.class));
+ any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -247,7 +247,7 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
- any(NetworkCapabilities.class), any(LinkProperties.class));
+ any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
manager.unregisterNetworkCallback(callback);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c77fcc..17bcea0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,6 +51,10 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static com.android.internal.util.TestUtils.waitForIdleLooper;
@@ -92,6 +96,7 @@
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
@@ -148,6 +153,7 @@
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -215,11 +221,13 @@
private MockNetworkAgent mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
+ private INetworkPolicyListener mPolicyListener;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ @Mock INetworkPolicyManager mNpm;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -934,6 +942,11 @@
}
@Override
+ protected Tethering makeTethering() {
+ return mock(Tethering.class);
+ }
+
+ @Override
protected int reserveNetId() {
while (true) {
final int netId = super.reserveNetId();
@@ -1023,6 +1036,20 @@
public void waitForIdle() {
waitForIdle(TIMEOUT_MS);
}
+
+ public void setUidRulesChanged(int uidRules) {
+ try {
+ mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ public void setRestrictBackgroundChanged(boolean restrictBackground) {
+ try {
+ mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ } catch (RemoteException ignored) {
+ }
+ }
}
/**
@@ -1055,12 +1082,18 @@
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
+
mService = new WrappedConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
- mock(INetworkPolicyManager.class),
+ mNpm,
mock(IpConnectivityLog.class));
+ final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
+ ArgumentCaptor.forClass(INetworkPolicyListener.class);
+ verify(mNpm).registerListener(policyListenerCaptor.capture());
+ mPolicyListener = policyListenerCaptor.getValue();
+
// Create local CM before sending system ready so that we can answer
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
@@ -1441,7 +1474,8 @@
RESUMED,
LOSING,
LOST,
- UNAVAILABLE
+ UNAVAILABLE,
+ BLOCKED_STATUS
}
private static class CallbackInfo {
@@ -1522,6 +1556,11 @@
setLastCallback(CallbackState.LOST, network, null);
}
+ @Override
+ public void onBlockedStatusChanged(Network network, boolean blocked) {
+ setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+ }
+
public Network getLastAvailableNetwork() {
return mLastAvailableNetwork;
}
@@ -1582,6 +1621,7 @@
// - onSuspended, iff the network was suspended when the callbacks fire.
// - onCapabilitiesChanged.
// - onLinkPropertiesChanged.
+ // - onBlockedStatusChanged.
//
// @param agent the network to expect the callbacks on.
// @param expectSuspended whether to expect a SUSPENDED callback.
@@ -1589,7 +1629,7 @@
// onCapabilitiesChanged callback.
// @param timeoutMs how long to wait for the callbacks.
void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
- boolean expectValidated, int timeoutMs) {
+ boolean expectValidated, boolean expectBlocked, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1600,19 +1640,28 @@
expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
}
expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+ expectBlockedStatusCallback(expectBlocked, agent);
}
// Expects the available callbacks (validated), plus onSuspended.
void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
- expectAvailableCallbacks(agent, true, expectValidated, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
}
void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, true, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
}
void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, false, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
}
// Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1623,6 +1672,9 @@
expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
+ // Implicitly check the network is allowed to use.
+ // TODO: should we need to consider if network is in blocked status in this case?
+ expectBlockedStatusCallback(false, agent);
NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
assertEquals(nc1, nc2);
}
@@ -1665,6 +1717,12 @@
fn.test((NetworkCapabilities) cbi.arg));
}
+ void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
+ boolean actualBlocked = (boolean) cbi.arg;
+ assertEquals(expectBlocked, actualBlocked);
+ }
+
void assertNoCallback() {
waitForIdle();
CallbackInfo c = mCallbacks.peek();
@@ -3223,7 +3281,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
// pass timeout and validate that UNAVAILABLE is not called
@@ -3243,7 +3301,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3802,6 +3860,7 @@
networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
networkAgent);
+ networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
networkCallback.assertNoCallback();
checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
@@ -4010,6 +4069,7 @@
mCellNetworkAgent);
CallbackInfo cbi = cellNetworkCallback.expectCallback(
CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4068,6 +4128,7 @@
mCellNetworkAgent);
CallbackInfo cbi = cellNetworkCallback.expectCallback(
CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4444,6 +4505,101 @@
mMockVpn.disconnect();
}
+ @Test
+ public void testNetworkBlockedStatus() {
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build();
+ mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_REJECT_ALL);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+ // ConnectivityService should cache it not to invoke the callback again.
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ cellNetworkCallback.assertNoCallback();
+
+ mService.setUidRulesChanged(RULE_NONE);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+ // Restrict the network based on UID rule and NOT_METERED capability change.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+ mCellNetworkAgent);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ mService.setUidRulesChanged(RULE_ALLOW_METERED);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_NONE);
+ cellNetworkCallback.assertNoCallback();
+
+ // Restrict the network based on BackgroundRestricted.
+ mService.setRestrictBackgroundChanged(true);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ mService.setRestrictBackgroundChanged(true);
+ cellNetworkCallback.assertNoCallback();
+ mService.setRestrictBackgroundChanged(false);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ }
+
+ @Test
+ public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ // No Networkcallbacks invoked before any network is active.
+ mService.setUidRulesChanged(RULE_REJECT_ALL);
+ mService.setUidRulesChanged(RULE_NONE);
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ defaultCallback.assertNoCallback();
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+
+ // Allow to use the network after switching to NOT_METERED network.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Switch to METERED network. Restrict the use of the network.
+ mWiFiNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
+
+ // Network becomes NOT_METERED.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ // Verify there's no Networkcallbacks invoked after data saver on/off.
+ mService.setRestrictBackgroundChanged(true);
+ mService.setRestrictBackgroundChanged(false);
+ defaultCallback.assertNoCallback();
+
+ mCellNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
/**
* Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
*/
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index c9987b8..b165c6b 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -2,10 +2,19 @@
include $(CLEAR_VARS)
+aapt2_results := $(call intermediates-dir-for,PACKAGING,aapt2_run_host_unit_tests)/result.xml
+
# Target for running host unit tests on post/pre-submit.
.PHONY: aapt2_run_host_unit_tests
-aapt2_run_host_unit_tests: PRIVATE_GTEST_OPTIONS := --gtest_output=xml:$(DIST_DIR)/gtest/aapt2_host_unit_tests_result.xml
-aapt2_run_host_unit_tests: $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
- -$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests $(PRIVATE_GTEST_OPTIONS) > /dev/null 2>&1
+aapt2_run_host_unit_tests: $(aapt2_results)
+
+$(call dist-for-goals,aapt2_run_host_unit_tests,$(aapt2_results):gtest/aapt2_host_unit_tests_result.xml)
+
+# Always run the tests again, even if they haven't changed
+$(aapt2_results): .KATI_IMPLICIT_OUTPUTS := $(aapt2_results)-nocache
+$(aapt2_results): $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
+ -$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests --gtest_output=xml:$@ > /dev/null 2>&1
+
+aapt2_results :=
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index a20b9b7..b353ff0 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -35,6 +35,43 @@
namespace aapt {
+static ApkFormat DetermineApkFormat(io::IFileCollection* apk) {
+ if (apk->FindFile(kApkResourceTablePath) != nullptr) {
+ return ApkFormat::kBinary;
+ } else if (apk->FindFile(kProtoResourceTablePath) != nullptr) {
+ return ApkFormat::kProto;
+ } else {
+ // If the resource table is not present, attempt to read the manifest.
+ io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
+ if (manifest_file == nullptr) {
+ return ApkFormat::kUnknown;
+ }
+
+ // First try in proto format.
+ std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
+ if (manifest_in != nullptr) {
+ pb::XmlNode pb_node;
+ io::ProtoInputStreamReader proto_reader(manifest_in.get());
+ if (proto_reader.ReadMessage(&pb_node)) {
+ return ApkFormat::kProto;
+ }
+ }
+
+ // If it didn't work, try in binary format.
+ std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+ if (manifest_data != nullptr) {
+ std::string error;
+ std::unique_ptr<xml::XmlResource> manifest =
+ xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
+ if (manifest != nullptr) {
+ return ApkFormat::kBinary;
+ }
+ }
+
+ return ApkFormat::kUnknown;
+ }
+}
+
std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) {
Source source(path);
std::string error;
@@ -301,41 +338,4 @@
return doc;
}
-ApkFormat LoadedApk::DetermineApkFormat(io::IFileCollection* apk) {
- if (apk->FindFile(kApkResourceTablePath) != nullptr) {
- return ApkFormat::kBinary;
- } else if (apk->FindFile(kProtoResourceTablePath) != nullptr) {
- return ApkFormat::kProto;
- } else {
- // If the resource table is not present, attempt to read the manifest.
- io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
- if (manifest_file == nullptr) {
- return ApkFormat::kUnknown;
- }
-
- // First try in proto format.
- std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
- if (manifest_in != nullptr) {
- pb::XmlNode pb_node;
- io::ProtoInputStreamReader proto_reader(manifest_in.get());
- if (!proto_reader.ReadMessage(&pb_node)) {
- return ApkFormat::kProto;
- }
- }
-
- // If it didn't work, try in binary format.
- std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
- if (manifest_data != nullptr) {
- std::string error;
- std::unique_ptr<xml::XmlResource> manifest =
- xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
- if (manifest != nullptr) {
- return ApkFormat::kBinary;
- }
- }
-
- return ApkFormat::kUnknown;
- }
-}
-
} // namespace aapt
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index 84c57c1..5b6f45e 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -121,8 +121,6 @@
std::unique_ptr<ResourceTable> table_;
std::unique_ptr<xml::XmlResource> manifest_;
ApkFormat format_;
-
- static ApkFormat DetermineApkFormat(io::IFileCollection* apk);
};
} // namespace aapt
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
new file mode 100755
index 0000000..48c0755
--- /dev/null
+++ b/tools/hiddenapi/merge_csv.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# 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.
+"""
+Merge mutliple CSV files, possibly with different columns, writing to stdout.
+"""
+
+import csv
+import sys
+
+csv_readers = [
+ csv.DictReader(open(csv_file, 'rb'), delimiter=',', quotechar='|')
+ for csv_file in sys.argv[1:]
+]
+
+# Build union of all columns from source files:
+headers = set()
+for reader in csv_readers:
+ headers = headers.union(reader.fieldnames)
+
+# Concatenate all files to output:
+out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', fieldnames = sorted(headers))
+out.writeheader()
+for reader in csv_readers:
+ for row in reader:
+ out.writerow(row)
+
+
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
index 76a2f2d..710da40 100755
--- a/tools/hiddenapi/sort_api.sh
+++ b/tools/hiddenapi/sort_api.sh
@@ -21,4 +21,6 @@
A=( ${C[*]} ${A[*]} )
unset IFS
# Dump array back into the file
-printf '%s\n' "${A[@]}" > "$dest_list"
+if [ ${#A[@]} -ne 0 ]; then
+ printf '%s\n' "${A[@]}" > "$dest_list"
+fi
diff --git a/wifi/java/android/net/wifi/WifiWakeReasonAndCounts.java b/wifi/java/android/net/wifi/WifiWakeReasonAndCounts.java
deleted file mode 100644
index f5cad13..0000000
--- a/wifi/java/android/net/wifi/WifiWakeReasonAndCounts.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2015 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.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A class representing wifi wake reason accounting.
- */
-
-/** @hide */
-public class WifiWakeReasonAndCounts implements Parcelable {
- private static final String TAG = "WifiWakeReasonAndCounts";
- /**
- * Wlan can wake host, only when it is cmd/event, local driver-fw
- * functions(non-data, non cmd/event) and rx data.The first packet
- * from wlan that woke up a sleep host is what is accounted here.
- * Total wlan wake to application processor would be:
- * [cmdEventWake + driverFwLocalWake + totalRxDataWake]
- * A further classification is provided for identifying the reasons
- * for wakeup.
- */
- public int totalCmdEventWake;
- public int totalDriverFwLocalWake;
- public int totalRxDataWake;
-
- public int rxUnicast;
- public int rxMulticast;
- public int rxBroadcast;
-
- public int icmp;
- public int icmp6;
- public int icmp6Ra;
- public int icmp6Na;
- public int icmp6Ns;
-
- public int ipv4RxMulticast;
- public int ipv6Multicast;
- public int otherRxMulticast;
- public int[] cmdEventWakeCntArray;
- public int[] driverFWLocalWakeCntArray;
-
- /* {@hide} */
- public WifiWakeReasonAndCounts () {
- }
-
- @Override
- /* {@hide} */
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append(" totalCmdEventWake ").append(totalCmdEventWake);
- sb.append(" totalDriverFwLocalWake ").append(totalDriverFwLocalWake);
- sb.append(" totalRxDataWake ").append(totalRxDataWake);
-
- sb.append(" rxUnicast ").append(rxUnicast);
- sb.append(" rxMulticast ").append(rxMulticast);
- sb.append(" rxBroadcast ").append(rxBroadcast);
-
- sb.append(" icmp ").append(icmp);
- sb.append(" icmp6 ").append(icmp6);
- sb.append(" icmp6Ra ").append(icmp6Ra);
- sb.append(" icmp6Na ").append(icmp6Na);
- sb.append(" icmp6Ns ").append(icmp6Ns);
-
- sb.append(" ipv4RxMulticast ").append(ipv4RxMulticast);
- sb.append(" ipv6Multicast ").append(ipv6Multicast);
- sb.append(" otherRxMulticast ").append(otherRxMulticast);
- for (int i = 0; i < cmdEventWakeCntArray.length; i++) {
- sb.append(" cmdEventWakeCntArray[" + i + "] " + cmdEventWakeCntArray[i]);
- }
- for (int i = 0; i < driverFWLocalWakeCntArray.length; i++) {
- sb.append(" driverFWLocalWakeCntArray[" + i + "] " + driverFWLocalWakeCntArray[i]);
- }
-
- return sb.toString();
- }
-
- /* Implement the Parcelable interface
- * {@hide}
- */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /* Implement the Parcelable interface
- * {@hide}
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(totalCmdEventWake);
- dest.writeInt(totalDriverFwLocalWake);
- dest.writeInt(totalRxDataWake);
-
- dest.writeInt(rxUnicast);
- dest.writeInt(rxMulticast);
- dest.writeInt(rxBroadcast);
-
- dest.writeInt(icmp);
- dest.writeInt(icmp6);
- dest.writeInt(icmp6Ra);
- dest.writeInt(icmp6Na);
- dest.writeInt(icmp6Ns);
-
- dest.writeInt(ipv4RxMulticast);
- dest.writeInt(ipv6Multicast);
- dest.writeInt(otherRxMulticast);
- dest.writeIntArray(cmdEventWakeCntArray);
- dest.writeIntArray(driverFWLocalWakeCntArray);
- }
-
- /* Implement the Parcelable interface
- * {@hide}
- */
- public static final Creator<WifiWakeReasonAndCounts> CREATOR =
- new Creator<WifiWakeReasonAndCounts>() {
- public WifiWakeReasonAndCounts createFromParcel(Parcel in) {
- WifiWakeReasonAndCounts counts = new WifiWakeReasonAndCounts();
- counts.totalCmdEventWake = in.readInt();
- counts.totalDriverFwLocalWake = in.readInt();
- counts.totalRxDataWake = in.readInt();
-
- counts.rxUnicast = in.readInt();
- counts.rxMulticast = in.readInt();
- counts.rxBroadcast = in.readInt();
-
- counts.icmp = in.readInt();
- counts.icmp6 = in.readInt();
- counts.icmp6Ra = in.readInt();
- counts.icmp6Na = in.readInt();
- counts.icmp6Ns = in.readInt();
-
- counts.ipv4RxMulticast = in.readInt();
- counts.ipv6Multicast = in.readInt();
- counts.otherRxMulticast = in.readInt();
- in.readIntArray(counts.cmdEventWakeCntArray);
- in.readIntArray(counts.driverFWLocalWakeCntArray);
- return counts;
- }
- /* Implement the Parcelable interface
- * {@hide}
- */
- @Override
- public WifiWakeReasonAndCounts[] newArray(int size) {
- return new WifiWakeReasonAndCounts[size];
- }
- };
-}