diff options
497 files changed, 20175 insertions, 11913 deletions
diff --git a/Android.bp b/Android.bp index f9b60e67e659..d94bd8439349 100644 --- a/Android.bp +++ b/Android.bp @@ -560,6 +560,7 @@ java_defaults { "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 @@ stubs_defaults { 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 d33307425968..770ec20f151e 100644 --- a/Android.mk +++ b/Android.mk @@ -87,6 +87,7 @@ $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \ 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 @@ $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \ --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 @@ $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \ $(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 ff40f7543c9d..5c212213f2d1 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -5,6 +5,7 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL packages/PrintRecommendationService/ packages/PrintSpooler/ packages/PackageInstaller/ + packages/SystemUI/ services/print/ services/usb/ telephony/ diff --git a/api/current.txt b/api/current.txt index 9269d979104f..51df940866d3 100755 --- a/api/current.txt +++ b/api/current.txt @@ -1199,6 +1199,7 @@ package android { 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 @@ package android { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.graphics.fonts { 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 @@ package android.graphics.fonts { 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 @@ package android.graphics.fonts { 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.pdf { } +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 @@ package android.media { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.opengl { 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 @@ package android.opengl { 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 @@ package android.opengl { 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 @@ package android.os { 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 @@ package android.provider { 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 @@ package android.provider { } 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 @@ package android.provider { 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 @@ package android.provider { } 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 @@ package android.provider { 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 @@ package android.telephony { 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 @@ package android.telephony { 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.data { 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 @@ package android.text { 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 @@ package android.text { 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 @@ package android.text.style { 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 @@ package android.text.style { 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 @@ package android.view.textclassifier { 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 @@ package android.view.textclassifier { 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 @@ package android.webkit { 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 83a789ad0294..26036ea9c281 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -222,7 +222,7 @@ package android { } 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 @@ package android { } 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 @@ package android.content { 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 @@ package android.os { 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 @@ package android.provider { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony.data { } 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 @@ package android.telephony.data { 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 @@ package android.telephony.ims { } 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 @@ package android.telephony.ims.feature { } 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.util { 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 @@ package android.view { 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 22465621e693..4e7a11444548 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -91,38 +91,6 @@ package android.os { } -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 c04e61b77274..41d546f6d603 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 @@ public class Am extends BaseCommand { 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 0dade0b2ba81..70baa8702ba9 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 @@ public class Instrument { 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 @@ public class Instrument { } // 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 db864aed6bdc..eba558653b04 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -33,6 +33,9 @@ LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \ 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_MODULE_TAGS := tests 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 f6b0db80f3ad..5818f5d550c3 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -46,6 +46,7 @@ statsd_common_src := \ 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 @@ LOCAL_SRC_FILES := \ 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 @@ LOCAL_SRC_FILES := \ 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 6a9e8a1bf688..3e8b9b8d5df5 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -194,7 +194,7 @@ private: 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 @@ private: 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 fb6f8c8d4590..ce2877731882 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -319,7 +319,7 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, } 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 957a9b434d05..dfb40a9bd16f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -143,10 +143,12 @@ message Atom { 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 @@ message Atom { 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 @@ message WakelockStateChanged { // 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 @@ message ResourceConfigurationChanged { // 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 @@ message ResourceConfigurationChanged { // 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 @@ message ResourceConfigurationChanged { // 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 @@ message ResourceConfigurationChanged { // 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 @@ message ResourceConfigurationChanged { // 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 @@ message BluetoothEnabledStateChanged { // 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 @@ message BatteryHealthSnapshot { } 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 @@ message PhoneStateChanged { } /** + * 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 @@ message AppStartMemoryStateCaptured { 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 @@ message LmkKillOccurred { 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 @@ message KernelWakelock { optional int32 version = 3; - optional int64 time = 4; + optional int64 time_micros = 4; } /** @@ -2246,11 +2304,11 @@ message WifiActivityInfo { // 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 WifiActivityInfo { 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 @@ message BluetoothActivityInfo { 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 @@ message ProcessMemoryState { 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 @@ message ProcessMemoryState { } /* + * 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 @@ message DiskSpace { * 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 @@ message RemainingBatteryCapacity { * 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 FullBatteryCapacity { */ message BatteryVoltage { // The voltage of the battery, in millivolts. - optional int32 voltage_mV = 1; + optional int32 voltage_millivolt = 1; } /** @@ -2445,7 +2528,7 @@ message Temperature { 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 @@ message LooperStats { // 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 @@ message LooperStats { // 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 @@ message ProcessStatsStateProto { 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 @@ message PackageServiceOperationStatsProto { 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 @@ message PackageAssociationSourceProcessStatsProto { 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 @@ message PackageAssociationSourceProcessStatsProto { // 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 ProcessStatsPackageProto { 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 PowerProfileProto { 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 dab64cacb679..1a9ba8a8de17 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -170,6 +170,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {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 000000000000..8418e9833509 --- /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 000000000000..57ec2b35ba32 --- /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 02b97734d3ff..f5a16e96fdf5 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -69,11 +69,16 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; 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 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric // 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 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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 @@ void GaugeMetricProducer::pullLocked(const int64_t timestampNs) { return; } for (const auto& data : allData) { - onMatchedLogEventLocked(0, *data); + if (mEventMatcherWizard->matchLogEvent( + *data, mWhatMatcherIndex) == MatchingState::kMatched) { + onMatchedLogEventLocked(mWhatMatcherIndex, *data); + } } } @@ -341,7 +349,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, 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 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition // 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 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven return; } for (const auto& data : allData) { - onMatchedLogEventLocked(0, *data); + if (mEventMatcherWizard->matchLogEvent( + *data, mWhatMatcherIndex) == MatchingState::kMatched) { + onMatchedLogEventLocked(mWhatMatcherIndex, *data); + } } } @@ -426,7 +437,7 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( 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 6379389218fd..99827bb2ad2d 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 @@ typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> 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 @@ public: flushCurrentBucketLocked(eventTimeNs); mCurrentBucketStartTimeNs = eventTimeNs; if (mIsPulled) { - pullLocked(eventTimeNs); + pullAndMatchEventsLocked(eventTimeNs); } }; @@ -113,7 +116,11 @@ private: 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 df081817232e..f87849edae7a 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -64,8 +64,54 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo 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 6fe4bfb47a1f..b21fd501c2d0 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -34,6 +34,17 @@ namespace android { 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 @@ public: mContainANYPositionInDimensionsInWhat(false), mSliceByPositionALL(false), mSameConditionDimensionsInTracker(false), - mHasLinksToAllConditionDimensionsInTracker(false) { + mHasLinksToAllConditionDimensionsInTracker(false), + mIsActive(true) { } virtual ~MetricProducer(){}; @@ -93,17 +105,23 @@ public: // 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 @@ public: 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 @@ protected: 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 @@ protected: /** * 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 @@ protected: 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 0e5ef4d3e59a..f85ba1f93e8c 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -73,7 +73,8 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, 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 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } 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 @@ void MetricsManager::onLogEvent(const LogEvent& event) { 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 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // 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 dfbb69f1ab7c..649222ffef9d 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -195,6 +195,11 @@ private: // 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 @@ private: 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 @@ private: 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 75d6df95852d..136ba074d589 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 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t 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 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } 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 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key, 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 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& 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 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& 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 c6601493135f..9ffcedae4962 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -108,6 +108,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& 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 1306a467e5c4..dffff7a96269 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 android { 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 @@ void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> 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. - std::unique_lock<std::mutex> lk(mMutex); + // 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 @@ void ShellSubscriber::cleanUpLocked() { mOutput = 0; mResultReceiver = nullptr; mPushedMatchers.clear(); + mPulledInfo.clear(); + mPullToken = 0; VLOG("done clean up"); } @@ -110,10 +195,13 @@ void ShellSubscriber::onLogEvent(const LogEvent& event) { 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 0ace35fab850..5401f31ce68c 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 @@ namespace statsd { * 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 @@ public: 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 @@ private: 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 516693d4e7f7..73cb49a61821 100644 --- a/cmds/statsd/src/shell/shell_config.proto +++ b/cmds/statsd/src/shell/shell_config.proto @@ -24,7 +24,7 @@ option java_outer_classname = "ShellConfig"; 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 000000000000..236bdbdd31f6 --- /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 d19e247ae6c7..d5f81a593082 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -347,6 +347,17 @@ message Subscription { 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 @@ message StatsdConfig { 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 8fbb58a956d5..f8184d8aa14c 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -282,13 +282,17 @@ TEST(MetricsManagerTest, TestGoodConfig) { 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 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { 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 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { 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 @@ TEST(MetricsManagerTest, TestMissingMatchers) { 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 @@ TEST(MetricsManagerTest, TestMissingPredicate) { 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 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { 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 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { 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 5b6f167984dc..d7b9c119b71b 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { 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 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { 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 000000000000..0f13a4ac1254 --- /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 bf58b9c7e4d4..60bd4a7e07d9 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 @@ namespace statsd { 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 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -178,7 +196,15 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -315,6 +348,12 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { 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 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { 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 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { 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 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { 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 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { 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 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { 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 b380b03e28d0..dd00561854fb 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 @@ public: } }; -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 @@ TEST(ShellSubscriberTest, testPushedSubscription) { 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 @@ TEST(ShellSubscriberTest, testPushedSubscription) { 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 @@ TEST(ShellSubscriberTest, testPushedSubscription) { // 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); + } + }); + + log_reader.detach(); - if (log_reader.joinable()) { - log_reader.join(); + 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 2b7da6ab6d79..4f4dd011e419 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 @@ public class MainActivity extends Activity { 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 @@ public class MainActivity extends Activity { 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 000000000000..f566bc7f2a53 --- /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 000000000000..0266d1143245 --- /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 000000000000..ae3e5a1b1dfa --- /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 b71274cf5dc2..bbc3f35aa05a 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -1383,62 +1383,6 @@ Landroid/service/wallpaper/IWallpaperEngine;->dispatchWallpaperCommand(Ljava/lan 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;->purge(Landroid/net/Uri;)Lcom/google/andr 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;->name:Ljava/lang/String; 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/Integer;->value:I 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/CharBuffer;->toString(II)Ljava/lang/String; 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 @@ Ljava/util/zip/ZipOutputStream;->written:J 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;->theEntities:Ljava/util/HashMap; 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 000000000000..e69de29bb2d1 --- /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 45e38cf95de0..e5e64d34f721 100644 --- a/config/hiddenapi-vendor-list.txt +++ b/config/hiddenapi-vendor-list.txt @@ -122,14 +122,6 @@ Landroid/os/UserHandle;->isSameApp(II)Z 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 56ca98ff9888..1a8a32ec574f 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -2354,6 +2354,7 @@ android.nfc.NfcAdapter$1 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 aca80b494b78..3cc5e370bc83 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4678,7 +4678,7 @@ public class Activity extends ContextThemeWrapper if (decor != null) { decor.cancelPendingInputEvents(); } - if (options != null && !isTopOfTask()) { + if (options != null) { mActivityTransitionState.startExitOutTransition(this, options); } } @@ -4882,6 +4882,7 @@ public class Activity extends ContextThemeWrapper Bundle options) throws IntentSender.SendIntentException { try { + options = transferSpringboardActivityOptions(options); String resolvedType = null; if (fillInIntent != null) { fillInIntent.migrateExtraStreamToClipData(); @@ -4898,6 +4899,12 @@ public class Activity extends ContextThemeWrapper 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 @@ public class Activity extends ContextThemeWrapper * * @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 14b8ae45d989..7330da323f47 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -137,6 +137,17 @@ public class ActivityManager { 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 784ce04908fe..069effd3ef19 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -22,6 +22,7 @@ import android.content.ComponentName; 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 class ActivityManagerInternal { 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 @@ public abstract class ActivityManagerInternal { /** * 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 @@ public abstract class ActivityManagerInternal { /** 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 @@ public abstract class ActivityManagerInternal { 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 3c9a2d4ab7e3..94b42ff960de 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -195,6 +195,13 @@ public class ActivityOptions { 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 @@ public class ActivityOptions { @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 @@ public class ActivityOptions { 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 @@ public class ActivityOptions { } /** + * 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 @@ public class ActivityOptions { 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 af8aa4e7c3b4..b8fe2f13d328 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -45,6 +45,12 @@ public class ActivityTaskManager { 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 ff6aca6fa6c4..9d3c5c6417af 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5054,7 +5054,7 @@ public final class ActivityThread extends ClientTransactionHandler { 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 @@ public final class ActivityThread extends ClientTransactionHandler { 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 9b2bfc5702cb..4b87a647a80b 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -193,6 +193,13 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { */ 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 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { 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 b8f5a8e94283..3201febec8da 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -35,7 +35,7 @@ import java.util.ArrayList; */ 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 @@ class ActivityTransitionState { /** * 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 @@ class ActivityTransitionState { 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 @@ class ActivityTransitionState { 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 @@ class ActivityTransitionState { } } + /** + * 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 @@ class ActivityTransitionState { } } else { mEnterTransitionCoordinator.namedViewsReady(null, null); - mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames(); + mPendingExitNames = null; } mExitingFrom = null; @@ -268,7 +279,7 @@ class ActivityTransitionState { } public void clear() { - mEnteringNames = null; + mPendingExitNames = null; mExitingFrom = null; mExitingTo = null; mExitingToView = null; @@ -296,7 +307,8 @@ class ActivityTransitionState { } 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 @@ class ActivityTransitionState { } 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 a05d01b5ad9a..a30ae799bd3d 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -431,9 +431,11 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { /** @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 @@ public class AppOpsManager { OP_CAMERA, // Body sensors OP_BODY_SENSORS, + // Activity recognition + OP_ACTIVITY_RECOGNITION, // APPOP PERMISSIONS OP_ACCESS_NOTIFICATIONS, @@ -819,6 +826,7 @@ public class AppOpsManager { OP_START_FOREGROUND, // START_FOREGROUND OP_COARSE_LOCATION, // BLUETOOTH_SCAN OP_USE_BIOMETRIC, // BIOMETRIC + OP_ACTIVITY_RECOGNITION, // ACTIVITY_RECOGNITION }; /** @@ -904,6 +912,7 @@ public class AppOpsManager { OPSTR_START_FOREGROUND, OPSTR_BLUETOOTH_SCAN, OPSTR_USE_BIOMETRIC, + OPSTR_ACTIVITY_RECOGNITION, }; /** @@ -990,6 +999,7 @@ public class AppOpsManager { "START_FOREGROUND", "BLUETOOTH_SCAN", "USE_BIOMETRIC", + "ACTIVITY_RECOGNITION", }; /** @@ -1077,6 +1087,7 @@ public class AppOpsManager { Manifest.permission.FOREGROUND_SERVICE, null, // no permission for OP_BLUETOOTH_SCAN Manifest.permission.USE_BIOMETRIC, + Manifest.permission.ACTIVITY_RECOGNITION, }; /** @@ -1164,6 +1175,7 @@ public class AppOpsManager { null, // START_FOREGROUND null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN null, // USE_BIOMETRIC + null, // ACTIVITY_RECOGNITION }; /** @@ -1250,6 +1262,7 @@ public class AppOpsManager { false, // START_FOREGROUND true, // BLUETOOTH_SCAN false, // USE_BIOMETRIC + false, // ACTIVITY_RECOGNITION }; /** @@ -1335,6 +1348,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // START_FOREGROUND AppOpsManager.MODE_ALLOWED, // BLUETOOTH_SCAN AppOpsManager.MODE_ALLOWED, // USE_BIOMETRIC + AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION }; /** @@ -1424,6 +1438,7 @@ public class AppOpsManager { 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 264029b6ace7..fcd9a0511265 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -55,6 +55,7 @@ import android.content.pm.ProviderInfo; 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.ErrnoException; 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 class ApplicationPackageManager extends PackageManager { 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 77f639535b15..dc707e892d9a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2088,8 +2088,7 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } @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 ab847fd562a4..bce243cc6108 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -65,6 +65,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { 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 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { case MSG_CANCEL: cancel(); break; + case MSG_ALLOW_RETURN_TRANSITION: + if (!mIsCanceled) { + mPendingExitNames = mAllSharedElementNames; + } + break; } } @@ -256,6 +262,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { 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 df31da9183f1..48a711e79c39 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -433,6 +433,11 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { 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 ceb828b64add..0e428ae164a4 100644 --- a/core/java/android/app/FragmentTransition.java +++ b/core/java/android/app/FragmentTransition.java @@ -1013,6 +1013,11 @@ class FragmentTransition { 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 b57a7d982960..4f41da6e52fb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -20,6 +20,7 @@ import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast; 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.pm.ShortcutInfo; 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 @@ public class Notification implements Parcelable * @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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable : 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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable 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 848def63177e..9f93e1765da3 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -603,9 +603,10 @@ public final class NotificationChannel implements Parcelable { } /** - * @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 0044005c51f2..77cebc8f408d 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -377,11 +377,15 @@ final class SystemServiceRegistry { 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 9873a8152b3f..e33d1fed4b4c 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 @@ public final class WallpaperInfo implements Parcelable { final int mContextDescriptionResource; final boolean mShowMetadataInPreview; final boolean mSupportsAmbientMode; + final String mSettingsSliceUri; /** * Constructor. @@ -118,7 +120,6 @@ public final class WallpaperInfo implements Parcelable { 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 @@ public final class WallpaperInfo implements Parcelable { 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 @@ public final class WallpaperInfo implements Parcelable { mContextDescriptionResource = source.readInt(); mShowMetadataInPreview = source.readInt() != 0; mSupportsAmbientMode = source.readInt() != 0; + mSettingsSliceUri = source.readString(); mService = ResolveInfo.CREATOR.createFromParcel(source); } @@ -332,13 +336,28 @@ public final class WallpaperInfo implements Parcelable { * 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 @@ public final class WallpaperInfo implements Parcelable { 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 74fb4df112b8..92daf08dc59b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -51,6 +51,7 @@ import android.content.pm.UserInfo; 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 class DevicePolicyManager { 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 @@ public class DevicePolicyManager { } 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 @@ public class DevicePolicyManager { 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 5e454506e9eb..ce1f4ef9e2e4 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -153,7 +153,7 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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 fda2f8927535..cb996f3381b7 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,6 +24,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothA2dpSink implements BluetoothProfile { 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 e7c8944788fd..c447868d6f0c 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -23,6 +23,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothAvrcpController implements BluetoothProfile { 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 9b7c173a1ec5..d616b8f92d3a 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -57,6 +57,7 @@ public final class BluetoothGatt implements BluetoothProfile { 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 @@ public final class BluetoothGatt implements BluetoothProfile { } } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } } @@ -357,7 +358,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -413,7 +414,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -496,7 +497,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -547,7 +548,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -596,7 +597,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -1098,7 +1099,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1132,7 +1133,7 @@ public final class BluetoothGatt implements BluetoothProfile { 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 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1221,7 +1222,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1262,7 +1263,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1330,7 +1331,7 @@ public final class BluetoothGatt implements BluetoothProfile { 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 ec18d42698c1..549c1faddd90 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -25,6 +25,7 @@ import android.os.Binder; 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 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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 b967fb20f023..22d41d9c896e 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -24,6 +24,7 @@ import android.os.Binder; 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 @@ public final class BluetoothHealth implements BluetoothProfile { 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 606f00a8239d..47c4ee6139d6 100644 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -29,6 +29,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothHearingAid implements BluetoothProfile { 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 3bc8544ebf87..e44f36e90c75 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -24,6 +24,7 @@ import android.content.Intent; 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 @@ public final class BluetoothHidDevice implements BluetoothProfile { 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 0ca39f169a72..58a25221552a 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -25,6 +25,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothHidHost implements BluetoothProfile { 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 98c23c600f14..fc5f830a8940 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -24,6 +24,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothMap implements BluetoothProfile { 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 559a59b68b4e..1c82e1984b66 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -25,6 +25,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothMapClient implements BluetoothProfile { 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 58be73296027..8923d734c844 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -26,6 +26,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothPan implements BluetoothProfile { 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 ae264e19bb7c..a601df02d032 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -24,6 +24,7 @@ import android.content.Intent; 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 @@ public class BluetoothPbap implements BluetoothProfile { 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 1446adc8b9c3..cbc96c073338 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothPbapClient implements BluetoothProfile { 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 1b732062f614..ebf6bed54475 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -24,6 +24,7 @@ import android.content.ServiceConnection; 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 @@ public final class BluetoothSap implements BluetoothProfile { 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 2a03787f134c..145c92731458 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -822,6 +822,48 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } /** + * 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 c19909da3007..599c2d2d3594 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -35,6 +35,12 @@ import android.database.ContentObserver; 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.RemoteException; 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.Retention; 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 @@ public abstract class ContentResolver { } 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 db4adcfaaf35..31973524ceaf 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4982,6 +4982,14 @@ public abstract class Context { 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 c5dce017430b..bfad2b42bc94 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -921,6 +921,14 @@ public class ContextWrapper extends Context { * @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 8913748e0c48..c0463e9ae7af 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4453,7 +4453,6 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ - @SystemApi public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID"; /** @@ -5009,8 +5008,7 @@ public class Intent implements Parcelable, Cloneable { "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 6a20c9349e1d..4a4de5160e80 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.PermissionGroupInfo; 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 @@ interface IPackageManager { 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 00aa5c291154..cdb781438909 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.graphics.drawable.Drawable; 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 @@ import java.util.Comparator; * 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 class PackageItemInfo { 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 class PackageItemInfo { */ 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 @@ public class PackageItemInfo { 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 @@ public class PackageItemInfo { @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); - } - - /** - * 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); - } + return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM + | SAFE_STRING_FLAG_FIRST_LINE); } /** - * Load, clean up and truncate label before use. - * - * <p>This method is meant to remove common mistakes and nefarious formatting from strings that - * are used in sensitive parts of the UI. - * - * <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 + * Calls {@link TextUtils#makeSafeForPresentation} for the label of this item. + * + * <p>For parameters see {@link TextUtils#makeSafeForPresentation}. + * * @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 733fbe566b09..dfb8128e37ee 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5664,7 +5664,7 @@ public abstract class PackageManager { * {@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 @@ public abstract class PackageManager { * 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 7b4c6fc64a69..4f58321da2f4 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -267,14 +267,15 @@ public abstract class PackageManagerInternal { 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 248d523a78ef..e21c33ad3bc1 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -33,6 +33,7 @@ import android.os.BaseBundle; 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 class PackageUserState { 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 @@ public class PackageUserState { installReason = PackageManager.INSTALL_REASON_UNKNOWN; } + @VisibleForTesting public PackageUserState(PackageUserState o) { ceDataInode = o.ceDataInode; installed = o.installed; @@ -87,7 +89,7 @@ public class PackageUserState { 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 @@ public class PackageUserState { || !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 000000000000..5e711cfb01c2 --- /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 000000000000..c798c99fed90 --- /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 09113e5d175d..01ef58e987d7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -397,7 +397,7 @@ public final class DisplayManager { 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 6609b76faac1..5de89e3422ab 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -105,12 +105,15 @@ public class ContextHubClient implements Closeable { * * 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 c496ff4a3bd5..47145874490f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2731,7 +2731,10 @@ public class ConnectivityManager { * * @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 @@ public class ConnectivityManager { * @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 @@ public class ConnectivityManager { } onCapabilitiesChanged(network, networkCapabilities); onLinkPropertiesChanged(network, linkProperties); + onBlockedStatusChanged(network, blocked); } /** @@ -2834,7 +2839,8 @@ public class ConnectivityManager { * 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 class ConnectivityManager { */ 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 class ConnectivityManager { 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 @@ public class ConnectivityManager { 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 @@ public class ConnectivityManager { 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 @@ public class ConnectivityManager { 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 12b6f9e1b370..0bdfca7f5025 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -1590,4 +1590,14 @@ public final class NetworkCapabilities implements Parcelable { 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 d912dd105fe9..1a1d2d33424c 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -202,7 +202,9 @@ public class NetworkInfo implements Parcelable { * 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 @@ public class NetworkInfo implements Parcelable { /** * 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 @@ public class NetworkInfo implements Parcelable { * 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 @@ public class NetworkInfo implements Parcelable { /** * 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 34e9476b3e08..c431e40ede2f 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 @@ public class NetworkUtils { } 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 40465ceafcb8..d09f33bcb755 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1102,19 +1102,18 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { 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 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } 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 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { 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 412a700de65b..292543c4a19a 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -130,9 +130,9 @@ public class Build { * <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 a9cb0d9579c9..f71fdd7fdac1 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1198,6 +1198,19 @@ public class FileUtils { /** {@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 5f656207255a..0c56d4884105 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -19,6 +19,8 @@ package android.os; 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 android.util.Log; 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 @@ public class GraphicsEnvironment { 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 @@ public class GraphicsEnvironment { * Set up GraphicsEnvironment */ public void setup(Context context, Bundle coreSettings) { - setupGpuLayers(context); + setupGpuLayers(context, coreSettings); setupAngle(context, coreSettings); chooseDriver(context); } @@ -81,27 +92,54 @@ public class GraphicsEnvironment { } /** + * 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 @@ public class GraphicsEnvironment { // 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 @@ public class GraphicsEnvironment { 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 @@ public class GraphicsEnvironment { 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 @@ public class GraphicsEnvironment { 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 @@ public class GraphicsEnvironment { } 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 @@ public class GraphicsEnvironment { 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 20ca19bc04aa..c9c42058bad0 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -388,10 +388,10 @@ interface INetworkManagementService /** * 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 @@ interface INetworkManagementService /** * 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 8123744281f4..00b989e03c61 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -272,6 +272,10 @@ public class UserManager { * * 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 class UserManager { 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 @@ public class UserManager { */ @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 50ca4abd6713..df771df5de0d 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1538,6 +1538,9 @@ public class StorageManager { * @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 @@ public class StorageManager { * @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 8c40e0e6cb8c..954d18abc6e1 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -34,6 +34,7 @@ import android.content.res.AssetFileDescriptor; 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.ErrnoException; 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 final class DocumentsContract { 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 @@ public final class DocumentsContract { /** {@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 828fd7386d80..f5660b950c0a 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -29,7 +29,6 @@ import android.content.ContentValues; 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.net.Uri; 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 @@ public final class MediaStore { * 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 @@ public final class MediaStore { * 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 @@ public final class MediaStore { * * @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 @@ public final class MediaStore { } } - 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 @@ public final class MediaStore { * 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 @@ public final class MediaStore { } /** - * 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 @@ public final class MediaStore { } /** - * 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 @@ public final class MediaStore { } /** - * 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. - */ - public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind, + * @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. + */ + @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. - */ - public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, + * @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. + */ + @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 @@ public final class MediaStore { * 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 @@ public final class MediaStore { * 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 @@ public final class MediaStore { /** * 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 @@ public final class MediaStore { } /** - * 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 @@ public final class MediaStore { } /** - * 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. - */ - public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind, + * @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. + */ + @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. - */ - public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, - int kind, BitmapFactory.Options options) { - return getThumbnail(cr, origId, kind, options); + * @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 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 @@ public final class MediaStore { /** * 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 acb75778e08e..ad640219a6a5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1473,12 +1473,13 @@ public final class Settings { * <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 final class Settings { 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 @@ public final class Settings { /** @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 final class Settings { 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 @@ public final class Settings { "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 @@ public final class Settings { } /** + * 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 c748c87e0805..035b226fc724 100644 --- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java +++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java @@ -113,19 +113,6 @@ public final class KeyChainSnapshot implements Parcelable { } /** - * 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 @@ public final class KeyChainSnapshot implements Parcelable { } /** - * 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 70054fc2d71e..31a5962c7e9a 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.annotation.SystemApi; 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 @@ public class RecoveryController { } /** - * @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 @@ public class RecoveryController { } /** - * @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 @@ public class RecoveryController { } /** - * @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 @@ public class RecoveryController { } /** - * @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 @@ public class RecoveryController { } /** - * @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 @@ public class RecoveryController { } /** - * 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 3bb64219cdca..42e718268d2d 100644 --- a/core/java/android/security/keystore/recovery/RecoverySession.java +++ b/core/java/android/security/keystore/recovery/RecoverySession.java @@ -78,36 +78,6 @@ public class RecoverySession implements AutoCloseable { } /** - * @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 @@ public class RecoverySession implements AutoCloseable { } /** - * @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 187a671c57cb..ae4448f9c908 100644 --- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java +++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java @@ -75,15 +75,6 @@ public final class WrappedApplicationKey implements Parcelable { } /** - * @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 @@ public final class WrappedApplicationKey implements Parcelable { 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 d7fe15d0e925..b8e03876a836 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -737,7 +737,8 @@ public abstract class NotificationListenerService extends Service { * <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 @@ public abstract class NotificationListenerService extends Service { * <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 10d7911316ac..ec63cd941b3f 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -1112,7 +1112,6 @@ public abstract class TextToSpeechService extends Service { @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 c89617fefec5..3ab8a0a8885f 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -23,6 +23,7 @@ import android.graphics.Canvas; 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 @@ import java.util.Arrays; 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 @@ public abstract class Layout { * 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 @@ public abstract class Layout { * 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 @@ public abstract class Layout { /** * 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 @@ public abstract class Layout { } } + /** * 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 @@ public abstract class Layout { 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 @@ public abstract class Layout { 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 9bf8cd20f441..f9370a8aa6af 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; 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 @@ public class MeasuredParagraph { 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 @@ public class MeasuredParagraph { mWidths.clear(); mFontMetrics.clear(); mSpanEndCache.clear(); - mNativeMeasuredParagraph = null; + mMeasuredText = null; } /** @@ -245,8 +246,8 @@ public class MeasuredParagraph { * 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 @@ public class MeasuredParagraph { * @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 @@ public class MeasuredParagraph { return r; } else { // We have result in native. - return mNativeMeasuredParagraph.getWidth(start, end); + return mMeasuredText.getWidth(start, end); } } @@ -281,7 +282,7 @@ public class MeasuredParagraph { */ 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 @@ public class MeasuredParagraph { * 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 @@ public class MeasuredParagraph { @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 @@ public class MeasuredParagraph { mt.mSpanEndCache.append(spanEnd); } } - mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation, - computeLayout); + mt.mMeasuredText = builder.build(); } return mt; @@ -490,7 +491,7 @@ public class MeasuredParagraph { 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 @@ public class MeasuredParagraph { } 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 @@ public class MeasuredParagraph { 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 @@ public class MeasuredParagraph { 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 @@ public class MeasuredParagraph { @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 @@ public class MeasuredParagraph { * 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 bfdccca2955b..000000000000 --- 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 d2f085369448..2cf0262a1d28 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; 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 @@ public class StaticLayout extends Layout { * * - 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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { .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 @@ public class StaticLayout extends Layout { 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 @@ public class StaticLayout extends Layout { 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 e31e928bb917..195de07d8595 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.CharacterStyle; 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.R; 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 @@ public class TextUtils { 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 @@ public class TextUtils { /** @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 @@ public class TextUtils { 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 @@ public class TextUtils { 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 9c7859fb11e4..5a55fd749150 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 648bd1bba20c..23557694a48d 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.ColorStateList; 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 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl private final Typeface mTypeface; private final int mTextFontWeight; + private final LocaleList mTextLocales; private final float mShadowRadius; private final float mShadowDx; @@ -149,6 +151,19 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl 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 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl mTypeface = null; mTextFontWeight = -1; + mTextLocales = null; mShadowRadius = 0.0f; mShadowDx = 0.0f; @@ -233,6 +249,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); mTextFontWeight = src.readInt(); + mTextLocales = src.readParcelable(LocaleList.class.getClassLoader()); mShadowRadius = src.readFloat(); mShadowDx = src.readFloat(); @@ -285,6 +302,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest); dest.writeInt(mTextFontWeight); + dest.writeParcelable(mTextLocales, flags); dest.writeFloat(mShadowRadius); dest.writeFloat(mShadowDx); @@ -349,6 +367,15 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl } /** + * 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 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl 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 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl 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 4608e205ec7c..0e5252e21a64 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -315,6 +315,7 @@ public class TransitionManager { 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 183e83304925..db2c19043361 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -47,7 +47,7 @@ public class FeatureFlagUtils { 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 209451bcfe6a..cd2b6ce3dc67 100644 --- a/core/java/android/util/proto/ProtoInputStream.java +++ b/core/java/android/util/proto/ProtoInputStream.java @@ -737,8 +737,7 @@ public final class ProtoInputStream extends ProtoStream { 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 @@ public final class ProtoInputStream extends ProtoStream { 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 3e559d9e106f..0c3a2957e1bc 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -226,25 +226,50 @@ interface IWindowManager 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. + * 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 isRotationFrozen(); + boolean isDisplayRotationFrozen(int displayId); /** * Screenshot the current wallpaper layer, including the whole screen. diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 29c58dc512fa..b59d8c720b9d 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1776,6 +1776,47 @@ public final class MotionEvent extends InputEvent implements Parcelable { 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 @@ public final class MotionEvent extends InputEvent implements Parcelable { 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 29d3742ba632..cc58b8928f6e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -24192,7 +24192,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 1b3e62d64ef3..58febb050150 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -541,11 +541,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager 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 982737aedc74..c1e94d8ff97e 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1085,6 +1085,19 @@ public abstract class Window { * * <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 @@ public abstract class Window { * @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 742df5e8a962..2d77cb4f3aca 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1670,7 +1670,7 @@ public interface WindowManager extends ViewManager { */ @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 interface WindowManager extends ViewManager { 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 @@ public interface WindowManager extends ViewManager { 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 ca2ccaf224db..e370e1175de5 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -48,6 +48,7 @@ import android.util.Pools.SimplePool; 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.WindowManager.LayoutParams.SoftInputModeFlags; 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.IInputMethodClient; 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 @@ public final class InputMethodManager { * @hide */ public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() { - getInstanceInternal(); + forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper()); } private static final Object sLock = new Object(); @@ -279,6 +282,17 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { // 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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { } 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 @@ public final class InputMethodManager { // 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 @@ public final class InputMethodManager { } @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 @@ public final class InputMethodManager { }); } - 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 @@ public final class InputMethodManager { * 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 @@ public final class InputMethodManager { 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 final class InputMethodManager { } 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 final class InputMethodManager { } 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 @@ public final class InputMethodManager { * 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 @@ public final class InputMethodManager { * {@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 @@ public final class InputMethodManager { * @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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { // 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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { @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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { // 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 @@ public final class InputMethodManager { 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 final class InputMethodManager { */ 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 @@ public final class InputMethodManager { * 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 @@ public final class InputMethodManager { */ @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 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { * @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 final class InputMethodManager { */ 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 @@ public final class InputMethodManager { 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 9692579de6ba..2e92f14c931c 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -21,7 +21,6 @@ import android.annotation.IntRange; 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 @@ public interface TextClassifier { 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 @@ public interface TextClassifier { * {@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 @@ public interface TextClassifier { 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 @@ public interface TextClassifier { 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 @@ public interface TextClassifier { } /** + * 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 @@ public interface TextClassifier { /** * 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 @@ public interface TextClassifier { /** * 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 @@ public interface TextClassifier { } /** @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 000000000000..d28459e733f0 --- /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 bdd7a0900213..300bb6fd4890 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -282,19 +282,28 @@ public class WebViewClient { 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 35ff6cc23499..4d031232152a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -42,6 +42,7 @@ import android.graphics.PorterDuff; 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 @@ public class RemoteViews implements Parcelable, Filter { 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 @@ public class RemoteViews implements Parcelable, Filter { // 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 @@ public class RemoteViews implements Parcelable, Filter { 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 @@ public class RemoteViews implements Parcelable, Filter { 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 @@ public class RemoteViews implements Parcelable, Filter { 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 @@ public class RemoteViews implements Parcelable, Filter { /** * @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 f74c2341d816..66809dbc4e45 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -63,7 +63,7 @@ import android.graphics.Rect; 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 10cf70215747..c256d57a6af6 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -137,7 +137,7 @@ public class Toast { 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 0f8295ac5868..7c371cb18878 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.os.ServiceManager; 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 @@ public class AssistUtils { 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 @@ public class AssistUtils { 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 a8edfb6ec936..498de53b65e9 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 @@ import com.android.internal.R; 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 @@ public class SuspendedAppActivity extends AlertActivity 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 @@ public class SuspendedAppActivity extends AlertActivity 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 class SuspendedAppActivity extends AlertActivity } 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 000000000000..a5dc3d1b7138 --- /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 000000000000..a01c45919b8f --- /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 000000000000..f0f18f11abe7 --- /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 46667d1ea688..2442d0b84bd6 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -158,7 +158,7 @@ public class KernelWakelockReader { 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 87515173e8de..3b7ce0a22b18 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -1387,7 +1387,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 2618356d7a09..17b2bc46de36 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -24,7 +24,6 @@ import com.android.internal.view.InputBindResult; */ 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 5f1243f37542..29c55c234677 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -31,7 +31,8 @@ import com.android.internal.view.IInputMethodClient; * 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 @@ interface IInputMethodManager { // 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 @@ interface IInputMethodManager { // 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 101fd41f2925..ec8e8dacb9db 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -51,6 +51,9 @@ public final class InputBindResult implements Parcelable { 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 @@ public final class InputBindResult implements Parcelable { * 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 @@ public final class InputBindResult implements Parcelable { 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 final class InputBindResult implements Parcelable { */ 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 bbd33a258e18..000000000000 --- 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 c029bb20d24c..3ea873bbb0f9 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 @@ public class Lists { * * @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 fc2c9feb8068..6ba33207631a 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 @@ public class Maps { * * @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 c2ca2fc35631..59c29e2626c7 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -45,6 +45,7 @@ cc_library_shared { "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 @@ cc_library_shared { "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 @@ cc_library_shared { "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 @@ cc_library_shared { shared_libs: [ "libbpf", + "libnetdbpf", "libnetdutils", "libmemtrack", "libandroidfw", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6b55ed6d2289..eada69097d05 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -77,6 +77,7 @@ extern int register_android_graphics_YuvImage(JNIEnv* env); 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_fonts_FontFamily(JNIEnv* env); 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_LocalSocketImpl(JNIEnv* env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 @@ static const RegJNIRec gRegJNI[] = { 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 @@ static const RegJNIRec gRegJNI[] = { 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 261910727db9..bb291e74ce0d 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 @@ public: 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 index 543910727ffd..e1f2f2b8e069 100644 --- a/core/jni/android_text_LineBreaker.cpp +++ b/core/jni/android/graphics/text/LineBreaker.cpp @@ -168,8 +168,9 @@ static const JNINativeMethod gMethods[] = { {"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 index 18f509c7ec60..0bfadb407a93 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 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong } // 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 @@ static const JNINativeMethod gMTBuilderMethods[] = { {"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 @@ static const JNINativeMethod gMTMethods[] = { {"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 000000000000..4a30babafa49 --- /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 b70485d9a0ea..e64da5ca0b24 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 @@ void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) { } 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 ecad6c027391..d023d22473c8 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 @@ static struct bindernative_offsets_t // Class state. jclass mClass; jmethodID mExecTransact; + jmethodID mGetInterfaceDescriptor; // Object state. jfieldID mObject; @@ -326,8 +328,32 @@ protected: 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 @@ protected: 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 @@ protected: private: JavaVM* const mVM; jobject const mObject; // GlobalRef to Java Binder + + mutable std::once_flag mPopulateDescriptor; + mutable String16 mDescriptor; }; // ---------------------------------------------------------------------------- @@ -926,6 +955,8 @@ static int int_register_android_os_Binder(JNIEnv* env) 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 109e65c4a1d0..b3ff4dbf8bef 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 a41edf30f913..1f63be93fb94 100644 --- a/core/proto/android/os/system_properties.proto +++ b/core/proto/android/os/system_properties.proto @@ -512,7 +512,9 @@ message SystemPropertiesProto { 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 a02602ee5d99..47dbc0716c94 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -397,9 +397,10 @@ message GlobalSettingsProto { // 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 d24b0026f939..1ae5f03a2f04 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 900ca2dd7667..dd79a0bb1817 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 b4e26483a2ad..5cb93eb9319a 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 3a0912bd2a90..01b0866f428c 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 cdb65edd81e7..32cf2e8bac86 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 aeeba59b694b..e257a5cdae34 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 2e42e4ac27f3..fd688a72b7ea 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 fa4406185218..9ea82a9b9c2e 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 6f28b2c36e16..81a1bf874a14 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 307e2e8671b2..041fb7eeb6b3 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -47,7 +47,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ 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 Binary files differnew file mode 100644 index 000000000000..459925433349 --- /dev/null +++ b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 6256d08adb00..0036186994fe 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; - -@Suppress // Failing. -public class ContentResolverTest extends AndroidTestCase { - private ContentResolver mContentResolver; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mContentResolver = mContext.getContentResolver(); - } - - @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); - } +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; + +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; + +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); + } + + @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 000000000000..c8a3098690be --- /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 80281b63c4a1..6966448f7d63 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.assertArrayEquals; 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 @@ public class FileUtilsTest { 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 60abd9468179..9778acba8213 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -368,6 +368,8 @@ public class SettingsBackupTest { 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 @@ public class SettingsBackupTest { 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 000000000000..1208d7ca194a --- /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 f3d6013b8ee3..3d15eb9577b5 100644 --- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java +++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java @@ -73,7 +73,7 @@ public class MeasuredParagraphTest { 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 @@ public class MeasuredParagraphTest { 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 @@ public class MeasuredParagraphTest { 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 @@ public class MeasuredParagraphTest { 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 @@ public class MeasuredParagraphTest { 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 @@ public class MeasuredParagraphTest { 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 c98e6460c189..7360e9f7d6f6 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.platform.test.annotations.Presubmit; 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 @@ public class ForwardDeleteTest { } @Test - @Suppress public void testEmojiModifier() { EditorState state = new EditorState(); @@ -256,20 +254,15 @@ public class ForwardDeleteTest { // 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 @@ public class ForwardDeleteTest { // 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 @@ public class ForwardDeleteTest { // 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 @@ public class ForwardDeleteTest { // 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 @@ public class ForwardDeleteTest { // 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 1a480c77ada0..023526f7e37f 100644 --- a/core/tests/coretests/src/android/view/MotionEventTest.java +++ b/core/tests/coretests/src/android/view/MotionEventTest.java @@ -54,6 +54,13 @@ public class MotionEventTest { 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 000000000000..75ca769294ce --- /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 1097bc731de8..01382aae923e 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.support.test.runner.AndroidJUnit4; 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 @@ public class KernelPackageMappingTests { } @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 0885a05c74f3..ea0a109c3a04 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -549,7 +549,7 @@ public abstract class BaseCanvas { 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 fb30ca2d4090..4de7ca708eb6 100644 --- a/graphics/java/android/graphics/BaseRecordingCanvas.java +++ b/graphics/java/android/graphics/BaseRecordingCanvas.java @@ -520,7 +520,7 @@ public class BaseRecordingCanvas extends Canvas { 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 b65fb9cd24ec..2e1d81a294e9 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -472,8 +472,8 @@ public abstract class ColorSpace { * <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 @@ public abstract class ColorSpace { "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 f291e2702948..ea9350174f97 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -19,6 +19,7 @@ package android.graphics; 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 @@ public class Point implements Parcelable { 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 7ad207f339d1..ba47300210cd 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -27,6 +27,7 @@ import android.annotation.NonNull; 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 @@ public class Typeface { @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 bd1ac25bf8df..f426b2d3465b 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -51,61 +51,6 @@ public final class Font { 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 @@ public final class Font { * <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 @@ public final class Font { * 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 @@ public final class Font { 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 @@ public final class Font { } 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 @@ public final class Font { 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 @@ public final class Font { * 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 @@ public final class Font { * @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 @@ public final class Font { 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 3bcdc31a3160..52a37da47cff 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -124,7 +124,7 @@ public final class FontFamily { } 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 000000000000..82fc7ac01772 --- /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 2d21bbbd4e43..750adb2757c8 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -192,7 +192,8 @@ public final class SystemFonts { 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 index 94e10e89acbd..16479095a63a 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.Retention; 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 class NativeLineBreaker { 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 class NativeLineBreaker { 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 class NativeLineBreaker { 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 @@ public class NativeLineBreaker { 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 @@ public class NativeLineBreaker { /** * 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 @@ public class NativeLineBreaker { /** * 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 @@ public class NativeLineBreaker { } /** - * 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 @@ public class NativeLineBreaker { } /** - * 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 @@ public class NativeLineBreaker { /** * 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 @@ public class NativeLineBreaker { * * @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 @@ public class NativeLineBreaker { * * @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 @@ public class NativeLineBreaker { * * @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 @@ public class NativeLineBreaker { * * @see #setIndent(float, int) */ - public @IntRange(from = 0) int getFirstWidthLineCount() { + public @Px @IntRange(from = 0) int getFirstWidthLineCount() { return mFirstWidthLineCount; } @@ -251,13 +329,14 @@ public class NativeLineBreaker { * * @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 @@ public class NativeLineBreaker { } /** - * Returns a number of line count. + * Returns the number of lines in the paragraph. * * @return number of lines */ @@ -283,7 +362,7 @@ public class NativeLineBreaker { } /** - * 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 @@ public class NativeLineBreaker { } /** - * 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 @@ public class NativeLineBreaker { } /** - * 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 @@ public class NativeLineBreaker { * * @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 @@ public class NativeLineBreaker { } 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 @@ public class NativeLineBreaker { * @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 000000000000..3efe655b2565 --- /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 8f58f74d4652..66a547723b2f 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -39,7 +39,7 @@ using base::unique_fd; 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 69702e314442..db2d0382bcf6 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 @@ class ApkAssets { // 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 03154d04def1..c221e3b7aeae 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 e3ec45b20883..f0053a48ae3d 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -202,9 +202,7 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "CanvasState.cpp", "CanvasTransform.cpp", - "ClipArea.cpp", "DamageAccumulator.cpp", "DeferredLayerUpdater.cpp", "DeviceInfo.cpp", @@ -227,14 +225,15 @@ cc_defaults { "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 @@ cc_test { "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 @@ cc_test { "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 d18c4abde7f2..000000000000 --- 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 9ac35ff47dab..000000000000 --- 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 27d93cfa0391..000000000000 --- 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 a7a11801cfe2..000000000000 --- 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 b424f97a5004..000000000000 --- 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 65bee476f14d..000000000000 --- 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 fd3f9fd05d58..000000000000 --- 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 f1a1bef7c94e..000000000000 --- 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 655f819ca41b..000000000000 --- 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 a6aae55b2ee8..f0912777e3d8 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 b04194f378bc..753557c2e120 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -224,6 +224,7 @@ Bitmap::~Bitmap() { 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 0022c931c45e..c6e4c154b41e 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -141,7 +141,7 @@ uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) { // 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 a00b8db3c617..c5db861d4f48 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 @@ public: 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 e3c97ce686d9..524dfb0fe4ef 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -288,7 +288,7 @@ void parseOptions(int argc, char* argv[]) { 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 9388c2062736..70423a70157b 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 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) } 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 4c03811b0c96..000000000000 --- 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 450bb679a45f..000000000000 --- 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 2926ef3a2d95..2c73940b9b9c 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 bc742b0c5a63..df5f45618070 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -35,23 +35,6 @@ SkBitmap createSkBitmap(int width, int height) { 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 9d673c82d4d0..000000000000 --- 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 1ae2a7bd8dee..0abf9aeb6101 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -9,4 +9,4 @@ sungsoo@google.com 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 c074ccebc62c..e8c97bc0161f 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3942,18 +3942,31 @@ public class AudioManager { } /** - * 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 569db16c312e..abd64119de61 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -151,8 +151,6 @@ interface IAudioService { 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 @@ interface IAudioService { 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 6d122d77a999..ee12b913765a 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -1192,15 +1192,17 @@ public final class MediaDrm implements AutoCloseable { 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 db6da8c590c3..e94413cd90fc 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -37,7 +37,6 @@ import java.lang.annotation.RetentionPolicy; 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 class MediaPlayer2 implements AutoCloseable 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 abstract class MediaPlayer2 implements AutoCloseable 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 5604ffd83deb..71df7dce5ea2 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -25,7 +25,6 @@ import android.content.Context; 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { @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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { case DataSourceDesc.TYPE_CALLBACK: handleDataSource(isCurrent, srcId, - dsd.getMedia2DataSource()); + dsd.getMedia2DataSource(), + dsd.getStartPosition(), + dsd.getEndPosition()); break; case DataSourceDesc.TYPE_FD: @@ -691,7 +692,9 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { srcId, dsd.getFileDescriptor(), dsd.getFileDescriptorOffset(), - dsd.getFileDescriptorLength()); + dsd.getFileDescriptorLength(), + dsd.getStartPosition(), + dsd.getEndPosition()); break; case DataSourceDesc.TYPE_URI: @@ -700,7 +703,9 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { dsd.getUriContext(), dsd.getUri(), dsd.getUriHeaders(), - dsd.getUriCookies()); + dsd.getUriCookies(), + dsd.getStartPosition(), + dsd.getEndPosition()); break; default: @@ -730,67 +735,77 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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)) { - return; - } - if (attemptDataSource(isCurrent, srcId, resolver, actualUri)) { - return; + 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"); } - handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies); - } else { - // Try requested Uri locally first, or fallback to media server - if (attemptDataSource(isCurrent, srcId, resolver, uri)) { + if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { ++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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { Media2HTTPService.createHTTPService(path, cookies), path, keys, - values); + values, + startPos, + endPos); return; } @@ -832,7 +850,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { 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 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { */ 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 66feb1ddf272..8664aa12343b 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -31,6 +31,7 @@ import android.content.Context; 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 @@ public class RingtoneManager { } /** + * 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 a7eb30d0f596..0c1d1a2fc8d2 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 e67b100d840d..474b671c0c2b 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -538,8 +538,13 @@ public class MtpDatabase implements AutoCloseable { 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 3870124f295e..fa9ab1f72688 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -128,6 +128,7 @@ cc_library_shared { "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 b52da362beff..61c28eddfeac 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -283,7 +283,8 @@ static void process_media_player_call( 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 @@ android_media_MediaPlayer2_handleDataSourceUrl( 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 @@ android_media_MediaPlayer2_handleDataSourceUrl( 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 @@ android_media_MediaPlayer2_handleDataSourceUrl( 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 @@ android_media_MediaPlayer2_handleDataSourceFD( 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 @@ android_media_MediaPlayer2_handleDataSourceFD( dsd->mFD = fd; dsd->mFDOffset = offset; dsd->mFDLength = length; + dsd->mStartPositionMs = startPos; + dsd->mEndPositionMs = endPos; status_t err; if (isCurrent) { @@ -402,7 +410,8 @@ android_media_MediaPlayer2_handleDataSourceFD( 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 @@ android_media_MediaPlayer2_handleDataSourceCallback( 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 @@ android_media_MediaPlayer2_isLooping(JNIEnv *env, jobject thiz) } 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 @@ static const JNINativeMethod gMethods[] = { { "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 @@ static const JNINativeMethod gMethods[] = { {"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 000000000000..9aae6ad0f080 --- /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 000000000000..731ce72aa043 --- /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 000000000000..472f9e71254f --- /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 33cb596415d5..4518d79ff611 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.telephony.TelephonyManager; 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 @@ public class CarrierActionUtils { 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 @@ public class CarrierActionUtils { 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 f9dbcd45b19b..5d84d6450574 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 @@ public class CarrierDefaultReceiverTest extends InstrumentationTestCase { 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 5782ea100070..31c9224a8489 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 class AgingHelper { 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 3333e1592bfa..8f33a7016b39 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 static android.service.notification.NotificationListenerService.Ranking 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 @@ public class Assistant extends NotificationAssistantService { } /** 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 @@ public class Assistant extends NotificationAssistantService { 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 2820232cdb38..2eb005a9b1fa 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.BufferedOutputStream; 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 @@ public class AssistantTest extends ServiceTestCase<Assistant> { 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 8fed367f3773..f2de9ecd8f5d 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java @@ -54,9 +54,14 @@ public class InstallStart extends Activity { 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 @@ public class InstallStart extends Activity { 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 8c29a2520390..441dbac24928 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 @@ public class PackageInstallerActivity extends AlertActivity { @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 d60dbe783fcd..5161344a4946 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -2,16 +2,14 @@ android_library { 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 7306d968a709..e407d7239546 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 @@ public class HelpUtils { 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 0f02abd94216..0748192fb1fc 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 b87c9e8de1b2..8529e3ef8420 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 @@ public class RestrictedLockUtils { 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 af30425a511c..cbebbb32dc89 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 e1f6cdf57101..5dbcb7952e88 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 f57122e6708d..1457fcfadc89 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -58,7 +58,7 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { 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 3102239c2c9e..3c451127508d 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.PackageItemInfo; 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 @@ public class DefaultAppInfo extends CandidateInfo { 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 @@ public class DefaultAppInfo extends CandidateInfo { 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 750a8438c00d..a2e30df9ac5c 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 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> 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 1091e1601b89..36b70dfe2297 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 @@ public class HelpUtilsTest { @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 01f0d78ede1a..a92a2dd8c11a 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 @@ package com.android.settingslib.applications; 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 @@ public class DefaultAppInfoTest { @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 8745a330311a..c996620c1a30 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 @@ class SettingsProtoDumpUtil { 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 @@ class SettingsProtoDumpUtil { 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 18ec9c34e98c..e0c4d72615c8 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 class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { 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 class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { @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 @@ public class SettingsProvider extends ContentProvider { }); } + 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 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { // 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 @@ public class SettingsProvider extends ContentProvider { } } + 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 @@ public class SettingsProvider extends ContentProvider { } 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 @@ public class SettingsProvider extends ContentProvider { } 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 @@ public class SettingsProvider extends ContentProvider { 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 389d627a9bd6..ae2ca3f87ae7 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.SystemClock; 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.nio.charset.StandardCharsets; 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 @@ final class SettingsState { 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 @@ final class SettingsState { 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 95569dc6d1b7..572a92452291 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 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { 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 dcc6cbafe41d..822c39b6a71d 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 2fbf42fc0f39..637830906124 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 5a28a5e28d91..7c8c23eba73b 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 @@ public abstract class RotationWatcher { 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 198a4e6cedb8..b1463a3c53ea 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -143,9 +143,6 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { 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 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { if (newHeight != mOldHeight) { animateChild(mOldHeight, newHeight); } + + post(() -> updatePaddingAndGravityIfTooTall()); post(() -> updatePosition()); } @@ -241,7 +240,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity); mSeparatedView.setLayoutParams(separatedViewLayoutParams); - setGravity(p.gravity); + setGravity(rotateGravityRight(getGravity())); } private void swapDimens(View v) { @@ -299,7 +298,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity); mSeparatedView.setLayoutParams(separatedViewLayoutParams); - setGravity(p.gravity); + setGravity(rotateGravityLeft(getGravity())); } private int rotateGravityLeft(int gravity) { @@ -447,6 +446,46 @@ public class HardwareUiLayout extends LinearLayout implements Tunable { 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 d351c4f3e3e6..1bf87506ab0c 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -259,6 +259,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis @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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } 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 6d790668995e..449ed8c3bcdb 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 @@ public class SlicePermissionActivity extends Activity implements OnClickListener .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 0215fda81485..3fe99445f49d 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -87,6 +87,8 @@ public class SwipeHelper implements Gefingerpoken { 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 @@ public class SwipeHelper implements Gefingerpoken { mDisableHwLayers = disableHwLayers; } + public boolean isSwipingInProgress() { + return mSwipingInProgress; + } + private float getPos(MotionEvent ev) { return mSwipeDirection == X ? ev.getX() : ev.getY(); } @@ -318,6 +324,7 @@ public class SwipeHelper implements Gefingerpoken { 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 @@ public class SwipeHelper implements Gefingerpoken { wasRemoved = row.isRemoved(); } if (!mCancelled || wasRemoved) { + mSwipingInProgress = false; mCallback.onChildDismissed(animView); } if (endAction != null) { @@ -626,6 +634,7 @@ public class SwipeHelper implements Gefingerpoken { !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 bb059809be2b..1e61a77a76cf 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 @@ public interface DozeHost { 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 cb91d7815be5..d69b1bfa64c3 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 @@ public class DozeTriggers implements DozeMachine.Part { mDozeHost.onDoubleTap(screenX, screenY); mMachine.wakeUp(); } else if (isPickup || isWakeLockScreen) { + mDozeHost.setPassiveInterrupt(true); mMachine.wakeUp(); } else { mDozeHost.extendPulse(); @@ -210,6 +211,7 @@ public class DozeTriggers implements DozeMachine.Part { case INITIALIZED: mBroadcastReceiver.register(mContext); mDozeHost.addCallback(mHostCallback); + mDozeHost.setPassiveInterrupt(false); checkTriggersAtInit(); break; case DOZE: @@ -219,6 +221,7 @@ public class DozeTriggers implements DozeMachine.Part { 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 4a6786832df0..df763151cdd7 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 @@ public class MediaProjectionPermissionActivity extends Activity ((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 774567ef8bb1..95029c013ab6 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 @@ import com.android.systemui.shared.plugins.PluginManagerImpl; 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 @@ public class PluginInitializerImpl implements PluginInitializer { @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 92f5cae4e165..15d2e66a82ce 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 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @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 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta 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 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @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 class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta 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 2c384d0f4d80..21a33b0271db 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 @@ public final class AmbientPulseManager extends AlertingNotificationManager { 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 30d9ef7bd7a3..8526afd34514 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 @@ public class DragDownHelper implements Gefingerpoken { 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 @@ public class DragDownHelper implements Gefingerpoken { 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 0c5f39198b4f..a00eac4adea0 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 @@ package com.android.systemui.statusbar; 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 @@ public class KeyguardIndicationController { private final DevicePolicyManager mDevicePolicyManager; private boolean mDozing; + private float mDarkAmount; /** * Creates a new KeyguardIndicationController and registers callbacks. @@ -298,6 +299,15 @@ public class KeyguardIndicationController { 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 @@ public class KeyguardIndicationController { } } + 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 @@ public class KeyguardIndicationController { 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 e89e6e89bc07..2db99453e36c 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 @@ public class NotificationMediaManager implements Dumpable { 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 7fa042655e53..1495abf9310c 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 @@ public class NotificationShelf extends ActivatableNotificationView implements 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 f69ad43ed79c..5b3082b04d58 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 @@ public class NotificationViewHierarchyManager { 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 d6719f0a03e1..78a5817c32b2 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.annotation.IntDef; 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 @@ public class StatusBarStateController { = (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 @@ public class StatusBarStateController { 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 @@ public class StatusBarStateController { 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,23 +177,49 @@ public class StatusBarStateController { return StatusBarState.toShortString(state); } + private class RankedListener { + private final StateListener listener; + private final int rank; + + private RankedListener(StateListener l, int r) { + listener = l; + 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); - } - - private class RankedListener { - private final StateListener listener; - private final int rank; - private RankedListener(StateListener l, int r) { - listener = l; - rank = r; - } + /** + * 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 d097c8e706ba..fbf12ed39561 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.ArrayMap; 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 class NotificationData { 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 class NotificationData { } } - 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 a3e982e77522..28d339aaeab2 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 @@ package com.android.systemui.statusbar.notification; 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.NotificationRemoteInputManager; 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 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } 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 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. 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 81208c4330c5..53ebe747c2e9 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 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow 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 bce613a33859..8110c1c98dec 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.AboveShelfChangedListener; 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.ExpandableViewState; 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,12 +440,59 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - 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; + cacheIsSystemNotification(); + } + + /** + * Inflate views based off the inflation flags set. Inflation happens asynchronously. + */ + public void inflateViews() { mNotificationInflater.inflateNotificationViews(); + } - cacheIsSystemNotification(); + /** + * 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); } /** @@ -581,7 +639,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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 46019e3b48ea..38d657b967a0 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.View; 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 @@ public abstract class ExpandableView extends FrameLayout { 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 4ef8dbb19318..78564515a2c2 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.content.Context; 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 @@ public class NotificationContentView extends FrameLayout { 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 @@ public class NotificationContentView extends FrameLayout { 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 class NotificationContentView extends FrameLayout { 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 @@ public class NotificationContentView extends FrameLayout { 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 aa4765a349b4..ea1892be1b1f 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.notification.NotificationData; 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 @@ import java.util.concurrent.atomic.AtomicInteger; 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { } @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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { } } - 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { @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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { onViewApplied(newView); } catch (Exception anotherException) { runningInflations.remove(inflationId); - handleInflationError(runningInflations, e, entry.notification, callback); + handleInflationError(runningInflations, e, row.getStatusBarNotification(), + callback); } } }; @@ -430,8 +566,9 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { * * @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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 class NotificationInflater { 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 class NotificationInflater { } 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { } @VisibleForTesting + @InflationFlag public int getReInflateFlags() { return mReInflateFlags; } @@ -642,10 +786,9 @@ public class NotificationInflater { 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 @@ public class NotificationInflater { @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 @@ public class NotificationInflater { } @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 2ca7282041cc..f76284dd1ffc 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 @@ public abstract class NotificationViewWrapper implements TransformableView { } 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 fa75c7131e09..cfb6d990a9a7 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.View; 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 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; * 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 @@ public interface NotificationListContainer { 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 @@ public interface NotificationListContainer { */ 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 33ac390de29f..659f6c75c703 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.ObjectAnimator; 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.os.ServiceManager; 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.view.animation.Interpolator; 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.classifier.FalsingManager; 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.notification.VisibilityLocationProvider; 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 @@ import java.util.function.BiConsumer; /** * 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup inflateEmptyShadeView(); inflateFooterView(); - mVisualStabilityManager.setVisibilityLocationProvider(this); + mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation); setLongPressListener(mEntryManager.getNotificationLongClicker()); } @@ -589,7 +587,7 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; } @@ -1245,11 +1243,6 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup * @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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup * @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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup * 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 @@ public class NotificationStackScrollLayout extends ViewGroup * @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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup * 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup } } + @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 @@ public class NotificationStackScrollLayout extends ViewGroup } } - @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 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void onHeightChanged(ExpandableView view, boolean needsAnimation) { updateContentHeight(); updateScrollPositionOnExpandInBottom(view); @@ -3936,7 +3923,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onReset(ExpandableView view) { updateAnimationState(view); updateChronometerForChild(view); @@ -3969,13 +3955,8 @@ public class NotificationStackScrollLayout extends ViewGroup @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 @@ public class NotificationStackScrollLayout extends ViewGroup @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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup } }; - 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 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onMenuReset(View row) { View translatingParentView = mSwipeHelper.getTranslatingParentView(); if (translatingParentView != null && row == translatingParentView) { @@ -5612,7 +5469,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onMenuShown(View row) { if (row instanceof ExpandableNotificationRow) { MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR, @@ -5621,9 +5477,11 @@ public class NotificationStackScrollLayout extends ViewGroup } 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 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onDragCancelled(View v) { mFalsingManager.onNotificatonStopDismissing(); - setSwipingInProgress(false); } /** @@ -5654,7 +5510,6 @@ public class NotificationStackScrollLayout extends ViewGroup * 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 @@ public class NotificationStackScrollLayout extends ViewGroup * @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 @@ public class NotificationStackScrollLayout extends ViewGroup 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 @@ public class NotificationStackScrollLayout extends ViewGroup } @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 @@ public class NotificationStackScrollLayout extends ViewGroup } @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 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onChildSnappedBack(View animView, float targetLeft) { mAmbientState.onDragFinished(animView); updateContinuousShadowDrawing(); @@ -5780,7 +5628,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { // Returning true prevents alpha fading. @@ -5788,7 +5635,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public float getFalsingThresholdFactor() { return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; } @@ -5797,5 +5643,199 @@ public class NotificationStackScrollLayout extends ViewGroup 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 028957d233ff..599da3b280be 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.internal.annotations.VisibleForTesting; 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 @@ class NotificationSwipeHelper extends SwipeHelper 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 @@ class NotificationSwipeHelper extends SwipeHelper @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 @@ class NotificationSwipeHelper extends SwipeHelper 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 1f3244f2177d..a15fd7083017 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.util.Property; 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.AnimatableProperty; 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 @@ public class ViewState { 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 4df1e3bda1a5..e4a5caaf7d71 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 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll 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 @@ public class HeadsUpTouchHelper implements Gefingerpoken { 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 @@ public class HeadsUpTouchHelper implements Gefingerpoken { 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 @@ public class HeadsUpTouchHelper implements Gefingerpoken { 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 ae1353dd220c..072343a8b101 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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL 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 33bc164d807c..836a55fde0f1 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 @@ public class KeyguardClockPositionAlgorithm { * 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 class KeyguardClockPositionAlgorithm { } 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 @@ public class KeyguardClockPositionAlgorithm { mDarkAmount = dark; mCurrentlySecure = secure; mPulsing = pulsing; - mBouncerTop = bouncerTop; + mEmptyDragAmount = emptyDragAmount; } public void run(Result result) { @@ -194,15 +189,14 @@ public class KeyguardClockPositionAlgorithm { } 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 6bccf31b04a6..80f35060b737 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 @@ public class NavigationBarFragment extends Fragment implements Callbacks { try { WindowManagerGlobal.getWindowManagerService() - .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId()); + .watchRotation(mRotationWatcher, getContext().getDisplayId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -284,8 +284,8 @@ public class NavigationBarFragment extends Fragment implements Callbacks { 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 c4efa9431317..e92656ae0c02 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.DockedStackExistsListener; 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.recents.RecentsOnboarding; 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 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav 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 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService); mConfiguration = new Configuration(); + mTmpLastConfiguration = new Configuration(); mConfiguration.updateFrom(context.getResources().getConfiguration()); mScreenPinningNotify = new ScreenPinningNotify(mContext); @@ -445,13 +445,13 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } 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 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav 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 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @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 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav 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 6d53cd373d05..e31bad65dbb2 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.PorterDuff; 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 @@ public class NotificationPanelView extends PanelView implements 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 @@ public class NotificationPanelView extends PanelView implements */ 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 @@ public class NotificationPanelView extends PanelView implements mInterpolatedDarkAmount, mStatusBar.isKeyguardCurrentlySecure(), mPulsing, - mBouncerTop); + mEmptyDragAmount); mClockPositionAlgorithm.run(mClockPositionResult); PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); @@ -1235,6 +1247,12 @@ public class NotificationPanelView extends PanelView implements 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 class NotificationPanelView extends PanelView implements } 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 @@ public class NotificationPanelView extends PanelView implements @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 @@ public class NotificationPanelView extends PanelView implements 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 @@ public class NotificationPanelView extends PanelView implements } 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 @@ public class NotificationPanelView extends PanelView implements 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 e3a7b75554d3..1bed26dd3474 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 @@ import java.util.function.Consumer; 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 class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo */ 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo 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 085f7b6394ef..ade063d9718f 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 @@ public enum ScrimState { // 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 @@ public enum ScrimState { @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 56bef2e8ea59..56e5a1e1bd6f 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 @@ public class StatusBar extends SystemUI implements DemoMode, 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 @@ public class StatusBar extends SystemUI implements DemoMode, 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 @@ public class StatusBar extends SystemUI implements DemoMode, } @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 @@ public class StatusBar extends SystemUI implements DemoMode, mAmbientPulseManager.releaseAllImmediately(); mVisualStabilityManager.setScreenOn(true); mNotificationPanel.setTouchAndAnimationDisabled(false); - mDozeServiceHost.stopDozing(); updateVisibleToUser(); updateIsKeyguard(); updateScrimController(); @@ -4341,34 +4379,6 @@ public class StatusBar extends SystemUI implements DemoMode, 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 @@ public class StatusBar extends SystemUI implements DemoMode, // 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 @@ public class StatusBar extends SystemUI implements DemoMode, } 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 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mAnimateWakeup; private boolean mAnimateScreenOff; private boolean mIgnoreTouchWhilePulsing; + private boolean mPassivelyInterrupted; @Override public String toString() { @@ -4505,6 +4520,11 @@ public class StatusBar extends SystemUI implements DemoMode, } @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 class StatusBar extends SystemUI implements DemoMode, public boolean shouldAnimateScreenOff() { return mAnimateScreenOff; } + + public boolean wasPassivelyInterrupted() { + return mPassivelyInterrupted; + } } public boolean shouldIgnoreTouch() { @@ -5267,8 +5291,7 @@ public class StatusBar extends SystemUI implements DemoMode, (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 57c7e285e29f..0d37b550d4e0 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 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat } 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 45b32c7abae0..ad9b9b30fafc 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.annotations.VisibleForTesting; 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 @@ public class StatusBarWindowView extends FrameLayout { } } + @VisibleForTesting + protected NotificationStackScrollLayout getStackScrollLayout() { + return mStackScrollLayout; + } + @Override public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); @@ -215,8 +221,11 @@ public class StatusBarWindowView extends FrameLayout { 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 @@ public class StatusBarWindowView extends FrameLayout { } } if (isDown) { - mStackScrollLayout.closeControlsIfOutsideTouch(ev); + getStackScrollLayout().closeControlsIfOutsideTouch(ev); } if (mService.isDozing()) { mService.mDozeScrimController.extendPulse(); @@ -331,13 +340,14 @@ public class StatusBarWindowView extends FrameLayout { @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 @@ public class StatusBarWindowView extends FrameLayout { 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 class StatusBarWindowView extends FrameLayout { } 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 d477587f8ecb..b4d24d16113e 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 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { 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 24a28cb45952..70a35892da98 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 @@ public class NetworkControllerImpl extends BroadcastReceiver 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 @@ public class NetworkControllerImpl extends BroadcastReceiver 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 af99236cc393..e85dee841715 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 class TunablePadding implements Tunable { 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 66d5ee1a276f..4102e63f7330 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 @@ public class UsbDebuggingActivity extends AlertActivity @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 a97effd3a023..e20e267336ea 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 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa 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 4810b0b91c10..798f8bcd7938 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 @@ public class VolumeDialogImpl implements VolumeDialog { 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 @@ public class VolumeDialogImpl implements VolumeDialog { 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 @@ public class VolumeDialogImpl implements VolumeDialog { 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 @@ public class VolumeDialogImpl implements VolumeDialog { 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 2398fd3c4712..6abd407afd91 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 @@ class DozeHostFake implements DozeHost { } @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 bdd05c7e6384..aae6d93cebed 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.Instrumentation; 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 com.android.systemui.util.wakelock.WakeLockFake; 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 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { 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 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @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 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { 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 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { }); 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 edf29ac1c4f8..aca1f90b5aa8 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.text.TextUtils; 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 @@ public class NotificationTestHelper { 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 @@ public class NotificationTestHelper { 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 @@ public class NotificationTestHelper { 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 15c18e96d709..6b4ccc4a80b5 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 class NotificationViewHierarchyManagerTest extends SysuiTestCase { 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 4e16b7f94283..f01ae7a23533 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.SmartReplyController; 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 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @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 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 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 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 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 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 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 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 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 743b307d0666..cfc75261123a 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 @@ package com.android.systemui.statusbar.notification.row; 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.AppOpsManager; import android.app.NotificationChannel; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.ArraySet; import android.view.NotificationHeaderView; @@ -134,6 +140,15 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @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 81e79d1490b9..150d9337d4a4 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.service.notification.StatusBarNotification; 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 @@ public class NotificationInflaterTest extends SysuiTestCase { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry) { + public void onAsyncInflationFinished(NotificationData.Entry entry, + @NotificationInflater.InflationFlag int inflatedFlags) { } }); } @@ -91,7 +96,7 @@ public class NotificationInflaterTest extends SysuiTestCase { 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 class NotificationInflaterTest extends SysuiTestCase { 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 @@ public class NotificationInflaterTest extends SysuiTestCase { } @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 @@ public class NotificationInflaterTest extends SysuiTestCase { 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 @@ public class NotificationInflaterTest extends SysuiTestCase { } @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 @@ public class NotificationInflaterTest extends SysuiTestCase { /* 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 @@ public class NotificationInflaterTest extends SysuiTestCase { } @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 f7a7e0430977..de26c709922d 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 @@ public class StatusBarWindowControllerTest extends SysuiTestCase { 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 445a194155bb..46335dc3b5ec 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.SysuiTestCase; 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 @@ public class StatusBarWindowViewTest extends SysuiTestCase { 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 d86de5dfd799..da1fee374520 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6564,6 +6564,11 @@ message MetricsEvent { // 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/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index da52d408e125..39866a72ab98 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.ParceledListSlice; 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 328f8f7a37a7..4168a3f4b28c 100644 --- a/services/art-profile +++ b/services/art-profile @@ -207,7 +207,7 @@ HPLandroid/hardware/weaver/V1_0/IWeaver;->read(ILjava/util/ArrayList;Landroid/ha 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/WeaverConfig;->readFromParcel(Landroid/os/HwParce 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 af33bd02c745..d3842b74990c 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 @@ public final class AutofillManagerService extends SystemService { 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 78facf84b03c..14d68cb853d6 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 @@ final class AutofillManagerServiceImpl { 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 @@ final class AutofillManagerServiceImpl { return null; } + @Nullable ComponentName getServiceComponentName() { synchronized (mLock) { if (mInfo == null) { @@ -706,17 +690,27 @@ final class AutofillManagerServiceImpl { } } - @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 @@ final class AutofillManagerServiceImpl { } 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 c1b620c657b3..f85749af54fd 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.Intent; 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState || 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - 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 a8dae03e4c12..2cc6d20ef6db 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 @@ final class ViewState { public final AutofillId id; private final Listener mListener; - private final Session mSession; private FillResponse mResponse; private AutofillValue mCurrentValue; @@ -87,8 +86,7 @@ final class ViewState { 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 @@ final class ViewState { 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 1b97926d39fb..eb31e782eb33 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.Set; 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // Cancel all running backups. - @Override public void cancelBackups() { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups"); if (MORE_DEBUG) { @@ -1732,7 +1725,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // synchronous waiter case - @Override public boolean waitUntilOperationComplete(int token) { if (MORE_DEBUG) { Slog.i(TAG, "Blocking until operation complete for " @@ -1895,7 +1886,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } - @Override public void tearDownAgentAndKill(ApplicationInfo app) { if (app == null) { // Null means the system package, so just quietly move on. :) @@ -2049,7 +2039,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { * @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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // ----- IBackupManager binder interface ----- - @Override public void dataChanged(final String packageName) { final int callingUserHandle = UserHandle.getCallingUserId(); if (callingUserHandle != UserHandle.USER_SYSTEM) { @@ -2362,7 +2349,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @Override public void fullTransportBackup(String[] pkgNames) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullTransportBackup"); @@ -2618,7 +2600,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @Override public void adbRestore(ParcelFileDescriptor fd) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore"); @@ -2719,7 +2700,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // Enable/disable backups - @Override public void setBackupEnabled(boolean enable) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "setBackupEnabled"); @@ -2887,7 +2866,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // Report whether the backup mechanism is currently enabled - @Override public boolean isBackupEnabled() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); @@ -2925,7 +2901,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // Report the name of the currently active transport - @Override public String getCurrentTransport() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "getCurrentTransport"); @@ -2938,7 +2913,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { * 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // Report all known, available backup transports - @Override public String[] listAllTransports() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); @@ -2962,14 +2935,12 @@ public class BackupManagerService implements BackupManagerServiceInterface { 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { * @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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } /** Selects transport {@code transportName} and returns previous selected transport. */ - @Override @Deprecated @Nullable public String selectBackupTransport(String transportName) { @@ -3089,7 +3058,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @Override public void selectBackupTransportAsync( ComponentName transportComponent, ISelectBackupTransportCallback listener) { mContext.enforceCallingOrSelfPermission( @@ -3161,7 +3129,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { * @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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { // 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 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @Override public boolean isAppEligibleForBackup(String packageName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "isAppEligibleForBackup"); @@ -3490,7 +3448,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @Override public String[] filterAppsEligibleForBackup(String[] packages) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup"); @@ -3517,7 +3474,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; @@ -3667,7 +3623,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { } - @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 a38a0e9918d2..000000000000 --- 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 fbec5cb22af2..bb145769fc8c 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 @@ public class KeyValueAdbRestoreEngine implements Runnable { 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 @@ public class KeyValueAdbRestoreEngine implements Runnable { 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 787d667afe51..818154b283da 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 @@ public class Trampoline extends IBackupManager.Stub { 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 @@ public class Trampoline extends IBackupManager.Stub { BACKUP_SUPPRESS_FILENAME); } - protected BackupManagerServiceInterface createBackupManagerService() { + protected BackupManagerService createBackupManagerService() { return BackupManagerService.create(mContext, this, mHandlerThread); } @@ -135,7 +135,7 @@ public class Trampoline extends IBackupManager.Stub { 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 @@ public class Trampoline extends IBackupManager.Stub { @Override public void dataChanged(String packageName) throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.dataChanged(packageName); } @@ -207,7 +207,7 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @Override public void agentDisconnected(String packageName) throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.agentDisconnected(packageName); } @@ -240,7 +240,7 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setBackupEnabled(boolean isEnabled) throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.setBackupEnabled(isEnabled); } @@ -256,7 +256,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setAutoRestore(boolean doAutoRestore) throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.setAutoRestore(doAutoRestore); } @@ -264,7 +264,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setBackupProvisioned(boolean isProvisioned) throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.setBackupProvisioned(isProvisioned); } @@ -272,25 +272,25 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { 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 @@ public class Trampoline extends IBackupManager.Stub { @Override public void fullTransportBackup(String[] packageNames) throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.fullTransportBackup(packageNames); } @@ -318,7 +318,7 @@ public class Trampoline extends IBackupManager.Stub { @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 class Trampoline extends IBackupManager.Stub { 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 @@ public class Trampoline extends IBackupManager.Stub { @Override public String getCurrentTransport() throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; return (svc != null) ? svc.getCurrentTransport() : null; } @@ -348,25 +348,25 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { String currentDestinationString, @Nullable Intent dataManagementIntent, String dataManagementLabel) { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.updateTransportAttributes( transportComponent, @@ -392,14 +392,14 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @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 @@ public class Trampoline extends IBackupManager.Stub { @Override public void cancelBackups() throws RemoteException { - BackupManagerServiceInterface svc = mService; + BackupManagerService svc = mService; if (svc != null) { svc.cancelBackups(); } @@ -492,7 +492,7 @@ public class Trampoline extends IBackupManager.Stub { 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 @@ public class Trampoline extends IBackupManager.Stub { // 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 4b77c69aba2b..1c8d99a538bf 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_SUSPENDED; 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.ArrayList; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub @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 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } }; + 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 @@ public class ConnectivityService extends IConnectivityManager.Stub "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 @@ public class ConnectivityService extends IConnectivityManager.Stub "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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy(); if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) { - mProxyTracker.sendProxyBroadcast(mProxyTracker.getDefaultProxy()); + mProxyTracker.sendProxyBroadcast(); } } @@ -3766,6 +3893,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setAirplaneMode(boolean enable) { - enforceConnectivityInternalPermission(); + enforceNetworkStackSettingsOrSetup(); final long ident = Binder.clearCallingIdentity(); try { final ContentResolver cr = mContext.getContentResolver(); @@ -4765,15 +4894,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 26421a2acb66..0b30ff5cc398 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -242,11 +242,9 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService static class Injector { private final Context mContext; + private ConnectivityService mConnectivityService; + private LocationManager mLocationManager; Injector(Context ctx) { mContext = ctx; @@ -1527,7 +1527,11 @@ public class DeviceIdleController extends SystemService } 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 @@ public class DeviceIdleController extends SystemService } 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } } + @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 @@ public class DeviceIdleController extends SystemService /** 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 @@ public class DeviceIdleController extends SystemService */ @VisibleForTesting void setLightStateForTest(int lightState) { - mLightState = lightState; + synchronized (this) { + mLightState = lightState; + } } @VisibleForTesting @@ -2429,12 +2447,6 @@ public class DeviceIdleController extends SystemService } } - /** Must only be used in tests. */ - @VisibleForTesting - void setLocationManagerForTest(LocationManager lm) { - mLocationManager = lm; - } - @VisibleForTesting int getState() { return mState; @@ -2486,18 +2498,19 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService /** 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 @@ public class DeviceIdleController extends SystemService 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 232c151f0418..93bdcbbb718e 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.ArraySet; 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.PackageProviderKey; 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 cf39e95fd735..f510d8355179 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -170,19 +170,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub */ 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub @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 @@ public class NetworkManagementService extends INetworkManagementService.Stub @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 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub @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 class NetworkManagementService extends INetworkManagementService.Stub 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()); + final String ifName = routeInfo.getInterface(); + final String dst = la.toString(); + final String nextHop; + if (routeInfo.hasGateway()) { - cmd.appendArg(routeInfo.getGateway().getHostAddress()); + 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 7c67596d92b4..858dcedd03cc 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3150,10 +3150,10 @@ class StorageManagerService extends IStorageManager.Stub 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 591ec0070ea5..65f3c035b031 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -216,6 +216,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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 class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + 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 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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 b2be5e6b5f8e..793a1778f900 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -1270,26 +1270,36 @@ public class VibratorService extends IVibratorService.Stub 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 @@ public class VibratorService extends IVibratorService.Stub } } + 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 @@ public class VibratorService extends IVibratorService.Stub 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 0b836f0d186f..9cc550daa5a7 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -528,7 +528,7 @@ public class Watchdog extends Thread { 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 fcda83d30f1c..3939bee52aa2 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -339,7 +339,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { 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 8e64b5063302..4070bca199e0 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.PrintWriter; 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { "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 @@ public final class ActiveServices { } } } - 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 @@ public final class ActiveServices { } } - 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } } - 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } if (anrMessage != null) { - mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage); + proc.appNotResponding(null, null, null, null, false, anrMessage); } } @@ -3666,7 +3666,7 @@ public final class ActiveServices { } 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 95a8e2a0aade..ede13ef66ac4 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.FOCUSED_STACK_ID; 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 @@ import java.util.ArrayList; */ 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> 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 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> * @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 3a0289cbfd63..5c77f0a3ad47 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 @@ final class ActivityManagerConstants extends ContentObserver { // 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 0e63d0c557f6..0aaea2f62de0 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 @@ class ActivityManagerDebugConfig { // 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 acf7a73332b7..4eb964ab461d 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 @@ package com.android.server.am; 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_PRIORITY_NORMAL; 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_BG_NONINTERACTIVE; 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.sendSignal; 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_BACKUP; 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_PROCESS_OBS 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_PROCESS_O 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.RunningTaskInfo; 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.NotificationManager; 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.IntentFilter; 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.ParcelFileDescriptor; 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.TransactionTooLargeException; 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.ArraySet; 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.ByteTransferPipe; 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.SystemServiceManager; 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 dalvik.system.VMRuntime; 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 class ActivityManagerService extends IActivityManager.Stub */ 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub */ 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") boolean mCallFinishBooting = false; @GuardedBy("this") boolean mBootAnimationComplete = false; - private @GuardedBy("this") boolean mCheckedForSetup = false; final Context mContext; @@ -1108,11 +1036,6 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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); - } + mProcessList.removeLruProcessLocked(app); } - 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 getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) { + return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge); } - 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; + final ProcessMap<ProcessRecord> getProcessNames() { + return mProcessList.mProcessNames; } void notifyPackageUse(String packageName, int reason) { @@ -3108,7 +2615,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - boolean getCheckedForSetup() { - return mCheckedForSetup; - } - - void setCheckedForSetup(boolean checked) { - mCheckedForSetup = checked; - } - CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) { return mAtmInternal.compatibilityInfoForPackage(ai); } @@ -3735,8 +2703,8 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub "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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub if (!fromBinderDied) { killProcessQuiet(pid); } - killProcessGroup(app.uid, pid); + ProcessList.killProcessGroup(app.uid, pid); app.killed = true; } @@ -4702,7 +3654,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); - startProcessLocked(app, "bind fail", processName); + mProcessList.startProcessLocked(app, "bind fail", processName); return false; } @@ -5890,9 +4551,7 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; + boolean providerRunning = false; synchronized(this) { long startTime = SystemClock.uptimeMillis(); @@ -7540,8 +6183,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - boolean providerRunning = false; - if (cpr != null && cpr.proc != null) { providerRunning = !cpr.proc.killed; @@ -7619,7 +6260,7 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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); - - for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord proc = mLruProcesses.get(i); + // 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 = 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } finally { Binder.restoreCallingIdentity(ident); } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mAtmInternal.resumeTopActivities(false /* scheduleIdle */); mUserController.sendUserSwitchBroadcasts(-1, currentUserId); BinderInternal.nSetBinderProxyCountWatermarks(6000,5500); @@ -9947,16 +8506,17 @@ public class ActivityManagerService extends IActivityManager.Stub : 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub .append(process.isInterestingToUserLocked() ? "Yes" : "No") .append("\n"); } - if (activity != null) { - sb.append("Activity: ").append(activity.shortComponentName).append("\n"); - } - if (parent != null && parent.app != null && parent.app.getPid() != process.pid) { - sb.append("Parent-Process: ").append(parent.app.mName).append("\n"); + if (activityShortComponentName != null) { + sb.append("Activity: ").append(activityShortComponentName).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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub proc = mPidsSelfLocked.get(Binder.getCallingPid()); } if (proc != null) { - fillInProcMemInfoLocked(proc, outState, clientTargetSdk); + mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk); } } } @@ -10629,24 +9102,26 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub "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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub "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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub + " 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub if (!r.pkgList.containsKey(dumpPackage)) { continue; } - r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED); + r.writeToProto(proto, + ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED); } } } @@ -11832,7 +10106,8 @@ public class ActivityManagerService extends IActivityManager.Stub || !r.pkgList.containsKey(dumpPackage))) { continue; } - it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS); + it.writeToProto(proto, + ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS); } } } @@ -11842,11 +10117,12 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - /** - * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub + " 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, - abiOverride); + mountExtStorageFull, abiOverride); app.setActiveInstrumentation(activeInstr); activeInstr.mFinished = false; activeInstr.mRunningProcesses.add(app); @@ -16613,6 +14731,11 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub processingBroadcasts = true; } } - return !processingBroadcasts - && (mAtmInternal.isSleeping() || mStackSupervisor.allResumedActivitiesIdle()); + return !processingBroadcasts && mAtmInternal.canGcNow(); } /** @@ -18178,7 +16302,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - return act; + return r; } /** @@ -18874,9 +17000,7 @@ public class ActivityManagerService extends IActivityManager.Stub */ @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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ public class ActivityManagerService extends IActivityManager.Stub uidRec.reset(); } - if (mStackSupervisor != null) { - mStackSupervisor.rankTaskLayersIfNeeded(); + if (mAtmInternal != null) { + mAtmInternal.rankTaskLayersIfNeeded(); } mAdjSeq++; @@ -18976,11 +17090,11 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME + if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME && !app.killedByAm) { numTrimming++; } @@ -19167,9 +17281,9 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } catch (RemoteException e) { } } - app.pendingUiClean = false; + app.setPendingUiClean(false); } if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) { try { @@ -19285,13 +17399,13 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } catch (RemoteException e) { } } - app.pendingUiClean = false; + app.setPendingUiClean(false); } app.trimMemoryLevel = 0; } @@ -19312,7 +17426,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub (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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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 40c555f8c2e6..96601a20471b 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 7080e2bd58fd..a0dd87811dae 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_T 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 @@ import com.android.server.LocalServices; */ 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { if (mWindowingModeTransitionInfo.size() == 0) { reset(true /* abort */, info); } - stopFullyDrawnTraceIfNeeded(); } } } @@ -509,14 +507,14 @@ class ActivityMetricsLogger { /** * 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { } /** - * 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { 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 6bdceb22d866..865c774a2267 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.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET; 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; 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 @@ import java.util.Objects; * 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } setVisible(true); sleeping = false; - app.setPendingUiClean(true); + app.postPendingUiCleanMsg(true); if (reportToClient) { makeClientVisible(); } else { @@ -2115,12 +2112,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } /** - * @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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo 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 864bf2d77b59..026c5cc3017d 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 android.view.WindowManager.TRANSIT_TASK_TO_FRONT; 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.PAUSE_IMMEDIATELY; 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 @@ import java.util.Set; */ 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai prev.getTask().touchActiveTime(); clearLaunchTime(prev); - mStackSupervisor.getActivityMetricsLogger().stopFullyDrawnTraceIfNeeded(); - mService.updateCpuStats(); if (prev.attachedToProcess()) { @@ -3878,8 +3877,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // 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 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai 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 8c8146c54d9f..257a0042b510 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.INTERNAL_SYSTEM_WINDOW; 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.INVALID_DISPLAY; 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.IS_HOME_RECENTS 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.ActivityManager.StackInfo; 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 @@ import java.util.Set; 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D beginDeferResume(); try { - final WindowProcessController proc = app.getWindowProcessController(); r.startFreezingScreenLocked(proc, 0); // schedule launch ticks to collect information about slow apps. @@ -1414,15 +1355,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } try { - if (app.thread == null) { + if (!proc.hasThread()) { throw new RemoteException(); } List<ResultInfo> results = null; @@ -1467,50 +1408,29 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } } - return finishedTask; + return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID; } void finishVoiceTask(IVoiceInteractionSession session) { @@ -2304,17 +2224,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } + 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D 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 f6f1e5508957..20d5ab201307 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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 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 @@ import java.util.List; * 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 @@ public class ActivityStartController { 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 @@ public class ActivityStartController { */ void startSetupActivity() { // Only do this once per boot. - if (mService.mAm.getCheckedForSetup()) { + if (mCheckedForSetup) { return; } @@ -203,7 +205,7 @@ public class ActivityStartController { 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 @@ public class ActivityStartController { 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 4789ff334398..e51824f6f790 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.IntentSender; 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 @@ class ActivityStartInterceptor { 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 33f949fccfb0..45a06524745a 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.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; 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 @@ import java.util.Date; * 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { } 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 @@ class ActivityStarter { || !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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { // 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, - preferredLaunchDisplayId, mTargetStack); + mPreferredDisplayId, mTargetStack); return START_SUCCESS; } @@ -1608,15 +1588,14 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 000000000000..cf727380ae6b --- /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 b369b7135b4e..f79d9aa9ba67 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_PINNED; 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.Build.VERSION_CODES.N; 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_IN_PLACE; 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.PRESERVE_WINDOWS; 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.WindowConfiguration; 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.Looper; 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.UpdateLock; 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.MetricsLogger; 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.LocalServices; 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.ActivityTaskManagerInternal; 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 @@ import java.util.Locale; * {@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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } // 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void setWindowManager(WindowManagerService wm) { mWindowManager = wm; mLockTaskController.setWindowManager(wm); + mStackSupervisor.setWindowManager(wm); + } + + void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) { + mUsageStatsInternal = usageStatsManager; } UserManagerService getUserManager() { @@ -763,7 +838,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { && 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } 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 class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } 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 class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { || transit == TRANSIT_TASK_TO_FRONT; } - void dumpSleepStates(PrintWriter pw, boolean testPssMode) { + 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.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) { - pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens); - if (mRunningVoice != null) { - pw.println(" mRunningVoice=" + mRunningVoice); - pw.println(" mVoiceWakeLock" + mVoiceWakeLock); + 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"); } - pw.println(" mSleeping=" + mSleeping); - pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode); - pw.println(" mVrController=" + mVrController); } } @@ -4428,14 +4670,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void finishHeavyWeightApp() { synchronized (mGlobalLock) { + if (mHeavyWeightProcess != null) { + mHeavyWeightProcess.finishActivities(); + } ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals( mHeavyWeightProcess); } @@ -5763,14 +5957,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 cde633dddb3b..a80a5b5211ea 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 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen 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 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen 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 6a9c8877ce5b..e8ec0574610c 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.ArraySet; 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { } 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 @@ class AppErrors { // 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { } else { // Huh. Process.killProcess(pid); - ActivityManagerService.killProcessGroup(uid, pid); + ProcessList.killProcessGroup(uid, pid); } } return true; @@ -582,27 +568,12 @@ class AppErrors { 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 @@ class AppErrors { * * @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 @@ class AppErrors { 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 @@ class AppErrors { + " 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 @@ class AppErrors { // 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 @@ class AppErrors { } } - 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 7c983ff427ad..cb76e2fafebd 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 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli 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 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli 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 e2035f67031a..a0977be0e25d 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 @@ public final class BroadcastQueue { @Override public void run() { - mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation); + mApp.appNotResponding(null, null, null, null, false, mAnnotation); } } @@ -286,7 +286,7 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { 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 536f3a93a3f1..3c4ab006a4b6 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.Slog; 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 @@ public final class CompatModePackages { 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 @@ public final class CompatModePackages { 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 09c152ef5135..48e26ed3d863 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 @@ final class CoreSettingsObserver extends ContentObserver { 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 @@ final class CoreSettingsObserver extends ContentObserver { 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 e5add58f287a..b39873fe4335 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.internal.R; 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 cfe282917f3b..28b2a42522eb 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 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 @@ import java.io.PrintWriter; */ 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 218d9080c2c0..68e897fdbd3e 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 @@ class LaunchParamsController { 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 643c922ad2ca..5b31d5fc593f 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.os.UserHandle.USER_CURRENT; 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 @@ import java.util.Arrays; * @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 a8e1ccca8b9d..85ee7e65ffe7 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 @@ import java.util.regex.Pattern; * 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 @@ final class MemoryStatUtil { 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 @@ final class MemoryStatUtil { 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 b9c6fa6020c4..2dcddff2f442 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 @@ public final class PendingIntentRecord extends IIntentSender.Stub { 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 3ac7885eba37..cb6061a47351 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -16,29 +16,95 @@ 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 java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.nio.ByteBuffer; +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.os.Build; -import android.os.SystemClock; -import com.android.internal.util.MemInfoReader; -import com.android.server.wm.WindowManagerService; - +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.os.SystemProperties; -import android.net.LocalSocketAddress; 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Activity manager code dealing with processes. */ @@ -47,7 +113,7 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 final class ProcessList { } 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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) { + if (openLmkdSocket() == false) { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } + continue; + } + + // 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 { - sLmkdOutputStream.write(buf.array(), 0, buf.position()); - return; - } catch (IOException ex) { - Slog.w(TAG, "Error writing to lowmemorykiller socket"); + 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 { - sLmkdSocket.close(); - } catch (IOException ex2) { + r.thread.setHttpProxy(host, port, exclList, pacFileUrl); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to update http proxy for: " + + r.info.processName); } + } + } + } - sLmkdSocket = null; + @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 667d3faefc9f..fa7a08bb12eb 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 @@ package com.android.server.am; 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.content.res.Configuration; 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 android.util.proto.ProtoOutputStream; 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { // 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { // 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { } } + @Override public void writeToProto(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(ProcessRecordProto.PID, pid); @@ -825,6 +838,13 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { @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 @@ final class ProcessRecord implements WindowProcessListener { 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 @@ final class ProcessRecord implements WindowProcessListener { * 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 dd13e9868b1d..57f939f4438b 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.content.Intent.FLAG_ACTIVITY_NEW_TASK; 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 @@ import java.util.concurrent.TimeUnit; * // '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 fa0cb47ade02..115216592e71 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.Manifest.permission.START_TASKS_FROM_RECENTS; 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 @@ import com.android.internal.annotations.VisibleForTesting; */ 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 fd34d180ebc0..111adecb933f 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.util.DisplayMetrics.DENSITY_DEFAULT; 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 @@ import java.util.List; * 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 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { 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 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { + 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 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { 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 05b0d598f878..5f5916331e67 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.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER 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 @@ import java.util.Objects; // 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 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // 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 3a897c4d525c..d2dd3db70118 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 @@ class UserController implements Handler.Callback { } 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 366f95a170ff..51d86d66e6c1 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 @@ final class VrController { * * @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 f6f4db6a2d37..1743ddeedd91 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 @@ package com.android.server.am; 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.ArraySet; 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 @@ import java.util.ArrayList; * 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mCurSchedGroup; } + public void setCurrentProcState(int curProcState) { + mCurProcState = curProcState; + } + + int getCurrentProcState() { + return mCurProcState; + } + public void setReportedProcState(int repProcState) { mRepProcState = repProcState; } @@ -201,6 +240,78 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mInstrumenting; } + public void setPerceptible(boolean perceptible) { + mPerceptible = perceptible; + } + + boolean isPerceptible() { + return mPerceptible; + } + @Override protected int getChildCount() { return 0; @@ -500,12 +619,19 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio 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,31 +658,52 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio WindowProcessListener::updateServiceConnectionActivities, mListener)); } - void setPendingUiClean(boolean pendingUiClean) { + void setPendingUiCleanAndForceProcessStateUpTo(int newState) { 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); + WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo, + mListener, newState); mAtm.mH.sendMessage(m); } - void setPendingUiCleanAndForceProcessStateUpTo(int newState) { + void setRemoved(boolean removed) { if (mListener == null) return; // Posting on handler so WM lock isn't held when we call into AM. final Message m = PooledLambda.obtainMessage( - WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo, - mListener, newState); + WindowProcessListener::setRemoved, mListener, removed); mAtm.mH.sendMessage(m); } - void setRemoved(boolean removed) { + 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::setRemoved, mListener, removed); + 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio 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 2de3e3763028..4a7e6e8a38fe 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 @@ public interface WindowProcessListener { /** 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 f0ff570e385b..f56d8e6a3ce2 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 @@ public class AudioService extends IAudioService.Stub } } - @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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub } 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 @@ public class AudioService extends IAudioService.Stub } makeA2dpDeviceAvailable(address, btDevice.getName(), "onSetA2dpSinkConnectionState"); - setCurrentAudioRouteName(btDevice.getAliasName()); } } } @@ -6105,25 +6101,35 @@ public class AudioService extends IAudioService.Stub 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 47e85b5fd3ab..15468ff05c08 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 class ProxyTracker { 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 @@ public class ProxyTracker { * * 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 @@ public class ProxyTracker { Binder.restoreCallingIdentity(token); } - sendProxyBroadcast(mGlobalProxy == null ? mDefaultProxy : proxyInfo); + sendProxyBroadcast(); } } @@ -296,14 +295,14 @@ public class ProxyTracker { && (!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 @@ public class ProxyTracker { 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 c16d3cd5b4a5..b148a2f3fff5 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 @@ public class HdmiControlService extends SystemService { 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 @@ public class HdmiControlService extends SystemService { @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 @@ public class HdmiControlService extends SystemService { } 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 d347a9188dee..f7e871d0b645 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 @@ final class SystemAudioAutoInitiationAction extends HdmiCecFeatureAction { @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 4913e8bda6ad..c20079e8a685 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 @@ public class InputManagerService extends IInputManager.Stub 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 @@ public class InputManagerService extends IInputManager.Stub /** * 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 @@ public class InputManagerService extends IInputManager.Stub 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 a9b0d5c42f73..d57214ea894c 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.View; 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.annotations.GuardedBy; 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.IInputMethodManager; 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IInputContext inputContext; final int uid; final int pid; + final int selfReportedDisplayId; final InputBinding binding; final ClientDeathRecipient clientDeathRecipient; @@ -430,16 +434,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * * @see #mCurFocusedWindow */ + @SoftInputModeFlags int mCurFocusedWindowSoftInputMode; /** @@ -515,6 +522,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags */ + @MissingMethodFlags int mCurInputContextMissingMethods; /** @@ -688,20 +696,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub resetDefaultImeLocked(mContext); } updateFromSettingsLocked(true); - try { - startInputInnerLocked(); - } catch (RuntimeException e) { - Slog.w(TAG, "Unexpected exception", e); - } } if (initialUserSwitch) { @@ -1585,12 +1590,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager, mSettings.getEnabledInputMethodListLocked(), currentUserId, mContext.getBasePackageName()); - - try { - startInputInnerLocked(); - } catch (RuntimeException e) { - Slog.w(TAG, "Unexpected exception", e); - } } } } @@ -1738,22 +1737,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * {@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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub clearCurMethodLocked(); } - void resetCurrentMethodAndClient( - /* @InputMethodClient.UnbindReason */ final int unbindClientReason) { + void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) { mCurMethodId = null; unbindCurrentMethodLocked(); unbindCurrentClientLocked(unbindClientReason); @@ -2165,7 +2170,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mLastBindTime = SystemClock.uptimeMillis(); mShowRequested = mInputShown; mInputShown = false; - unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_DISCONNECT_IME); + unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); } } } @@ -2476,12 +2481,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void showInputMethodAndSubtypeEnablerFromClient( IInputMethodClient client, String inputMethodId) { + // TODO(yukawa): Should we verify the display ID? if (!calledFromValidUser()) { return; } @@ -3229,6 +3244,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ @Override public int getInputMethodWindowVisibleHeight() { + // TODO(yukawa): Should we verify the display ID? return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); } @@ -4564,7 +4580,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 0c66c5b22d76..6989c334d876 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 @@ import java.util.function.Predicate; * 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 @@ public final class ConnectivityController extends StateController implements 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 final class ConnectivityController extends StateController implements 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 final class ConnectivityController extends StateController implements 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 @@ public final class ConnectivityController extends StateController implements /** * 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 @@ public final class ConnectivityController extends StateController implements } } + 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 final class ConnectivityController extends StateController implements 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 final class ConnectivityController extends StateController implements 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 @@ public final class ConnectivityController extends StateController implements 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 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); + 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); } - 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 3f8941dfb21b..4ece538d6d31 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 @@ import java.util.function.Predicate; * 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 @@ public final class JobStatus { // 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 @@ public final class JobStatus { 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 @@ public final class JobStatus { 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 @@ public final class JobStatus { } 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 @@ public final class JobStatus { 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 @@ public final class JobStatus { return false; } satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0); + mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST; return true; } @@ -933,24 +977,15 @@ public final class JobStatus { /** * @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 @@ public final class JobStatus { 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 b29b7cf5a712..99a04d7b144f 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.content.Context; 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 @@ public class ContextHubClientBroker extends IContextHubClient.Stub 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 @@ public class ContextHubClientBroker extends IContextHubClient.Stub /* 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 @@ public class ContextHubClientBroker extends IContextHubClient.Stub 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 @@ public class ContextHubClientBroker extends IContextHubClient.Stub * @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 4243f02a15b2..eda8c6f8b418 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 @@ package com.android.server.location; 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 @@ import java.util.function.Consumer; * 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 @@ import java.util.function.Consumer; * 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 @@ import java.util.function.Consumer; 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 27509deb958f..96e9337a7932 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 @@ public class ContextHubService extends IContextHubService.Stub { 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 @@ public class ContextHubService extends IContextHubService.Stub { 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 af06d157a526..7304f0788a1d 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.Intent; 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.text.TextUtils; 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 @@ public class MediaUpdateService extends SystemService { 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 @@ public class MediaUpdateService extends SystemService { } } 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 @@ public class MediaUpdateService extends SystemService { // 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 @@ public class MediaUpdateService extends SystemService { null /* broadcast permission */, null /* handler */); } - private void packageStateChanged() { + private void packageStateChanged(String serviceName) { ApplicationInfo packageInfo = null; boolean pluginsAvailable = false; try { @@ -144,17 +153,23 @@ public class MediaUpdateService extends SystemService { + " 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 452b699667b7..4f4b6bfdb358 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 @@ public class NetworkPolicyLogger { 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 61d67b74da18..099671d81a3e 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 class NetworkPolicyManagerInternal { 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 48e09d744ff4..d7996422870c 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.internal.util.XmlUtils.writeStringAttribute; 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long startTime = mStatLogger.getTime(); mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered); + final int uidRules; + final boolean isBackgroundRestricted; + synchronized (mUidRulesFirstLock) { + uidRules = mUidRules.get(uid, RULE_NONE); + isBackgroundRestricted = mRestrictBackground; + } + final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, + isBackgroundRestricted, mLogger); 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; + 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 (hasRule(uidRules, RULE_REJECT_ALL)) { - mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER); - return true; + else if (hasRule(uidRules, RULE_REJECT_ALL)) { + reason = NTWK_BLOCKED_POWER; } - if (!isNetworkMetered) { - mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED); - return false; + else if (!isNetworkMetered) { + reason = NTWK_ALLOWED_NON_METERED; } - if (hasRule(uidRules, RULE_REJECT_METERED)) { - mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST); - return true; + else if (hasRule(uidRules, RULE_REJECT_METERED)) { + reason = NTWK_BLOCKED_BLACKLIST; } - if (hasRule(uidRules, RULE_ALLOW_METERED)) { - mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST); - return false; + else if (hasRule(uidRules, RULE_ALLOW_METERED)) { + reason = NTWK_ALLOWED_WHITELIST; } - if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { - mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST); - return false; + else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { + reason = NTWK_ALLOWED_TMP_WHITELIST; } - if (isBackgroundRestricted) { - mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT); - return true; + else if (isBackgroundRestricted) { + reason = NTWK_BLOCKED_BG_RESTRICT; } - mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT); - return false; + 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 class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 506cc441c868..93b83ae72cca 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { Preconditions.checkNotNull(pkg); Preconditions.checkNotNull(user); - verifyPrivilegedListener(token, user); + verifyPrivilegedListener(token, user, false); updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true); } @@ -3646,7 +3644,7 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 65ec5808a0dd..e9f2718fe2b3 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 final class NotificationRecord { } public void applyAdjustments() { + long now = System.currentTimeMillis(); synchronized (mAdjustments) { for (Adjustment adjustment: mAdjustments) { Bundle signals = adjustment.getSignals(); @@ -618,17 +619,25 @@ public final class NotificationRecord { 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 @@ public final class NotificationRecord { && (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 @@ public final class NotificationRecord { 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 @@ public final class NotificationRecord { 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 760f1559c845..44b80c138346 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 @@ public class ZenModeHelper { } 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 a08c189cb125..404f152bb8d1 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 33bb15f10554..9399ebf5b413 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.SELinuxUtil; 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 @@ public class PackageManagerService extends IPackageManager.Stub } } } - if (deleteSandboxData) { + if (deleteSandboxData && getStorageManagerInternal() != null) { getStorageManagerInternal().destroySandboxForApp(pkg.packageName, realUserId); } } catch (PackageManagerException e) { @@ -12157,7 +12158,9 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub @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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub false /*hidden*/, false /*suspended*/, null /*suspendingPackage*/, - null /*dialogMessage*/, + null /*dialogInfo*/, null /*suspendedAppExtras*/, null /*suspendedLauncherExtras*/, false /*instantApp*/, @@ -22581,10 +22586,10 @@ public class PackageManagerService extends IPackageManager.Stub } @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 93729d1949b0..e25cca43e8da 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.ParceledListSlice; 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 @@ class PackageManagerShellCommand extends ShellCommand { } 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 fd6aceb1ce6b..3c22f07ad108 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.PackageManager; 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 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { 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 5c88e0637092..e2818b70deb1 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.PackageUserState; 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { false /*hidden*/, false /*suspended*/, null /*suspendingPackage*/, - null /*dialogMessage*/, + null /*dialogInfo*/, null /*suspendedAppExtras*/, null /*suspendedLauncherExtras*/, instantApp, @@ -1620,7 +1626,7 @@ public final class Settings { false /*hidden*/, false /*suspended*/, null /*suspendingPackage*/, - null /*dialogMessage*/, + null /*dialogInfo*/, null /*suspendedAppExtras*/, null /*suspendedLauncherExtras*/, false /*instantApp*/, @@ -1730,6 +1736,7 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { } } + 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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 1a8b2af5a3a9..32826e51f0a4 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.service.pm.PackageServiceDumpProto; 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 @@ public final class SharedUserSetting extends SettingBase { } } + /** 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 13155027a387..dd04652a29b3 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 @@ public class UserRestrictionsUtils { 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 @@ public class UserRestrictionsUtils { * 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 @@ public class UserRestrictionsUtils { 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 0a93653ede56..e6a018a9f39d 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.Intent; 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 @@ public final class DefaultPermissionGrantPolicy { 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 @@ public final class DefaultPermissionGrantPolicy { && 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 06ee935818b3..c5cee32f1596 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }); 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @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 27ab3efb3fb1..db13cbccf993 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.IApplicationToken; 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 interface WindowManagerPolicy extends WindowManagerPolicyConstants { 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 interface WindowManagerPolicy extends WindowManagerPolicyConstants { 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 be24c7125969..5569822300b9 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 @@ public class BatterySaverController implements BatterySaverPolicyListener { } /** - * 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 @@ public class BatterySaverController implements BatterySaverPolicyListener { * 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 1abaaf2412bf..c162afbf3e61 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.Binder; 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 class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 e5347cfee5a2..7f9ee8405dfc 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 @@ package com.android.server.wm; 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.pm.ApplicationInfo; 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 class ActivityTaskManagerInternal { 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 @@ public abstract class ActivityTaskManagerInternal { /** @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 642f57814c61..ca2336029ed9 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.Display; 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 bd82553b804b..847cff9c6646 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 @@ public class DisplayRotation { 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 @@ public class DisplayRotation { 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 @@ public class DisplayRotation { } } + 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 @@ public class DisplayRotation { * @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 @@ public class DisplayRotation { 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 bbb690f6a34a..28dc008fda9b 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_WITH_CLASS_NAME 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.io.IOException; 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 @@ class DisplaySettings { 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 @@ class DisplaySettings { 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 @@ class DisplaySettings { 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); - return; - } Entry entry = mEntries.get(uniqueId); - if (entry == null) { - entry = new Entry(uniqueId); - mEntries.put(uniqueId, entry); + if (left == 0 && top == 0 && right == 0 && bottom == 0 && entry == null) { + // All default value, no action needed. + return; } - 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 @@ class DisplaySettings { 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 @@ class DisplaySettings { displayInfo.overscanTop = rect.top; displayInfo.overscanRight = rect.right; displayInfo.overscanBottom = rect.bottom; + + restoreUserRotation(dc); } void readSettingsLocked() { @@ -244,12 +296,16 @@ class DisplaySettings { 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 @@ class DisplaySettings { 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.mOverscanTop != 0) { + out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop)); + } + if (entry.mOverscanRight != 0) { + out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight)); } - if (entry.overscanTop != 0) { - out.attribute(null, "overscanTop", Integer.toString(entry.overscanTop)); + if (entry.mOverscanBottom != 0) { + out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom)); } - if (entry.overscanRight != 0) { - out.attribute(null, "overscanRight", Integer.toString(entry.overscanRight)); + if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) { + out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode)); } - if (entry.overscanBottom != 0) { - out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom)); + if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) { + out.attribute(null, "userRotationMode", + Integer.toString(entry.mUserRotationMode)); } - if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode)); + 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 b5e2f01d8fbb..10d77e59b0b3 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 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal } // 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 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal } 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 15f693872158..ed3e6c6ad810 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 class InputMonitor { 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 6fef16304d42..5c80759c6998 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 @@ public class RecentsAnimationController implements DeathRecipient { 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 @@ public class RecentsAnimationController implements DeathRecipient { @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 @@ public class RecentsAnimationController implements DeathRecipient { && 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 @@ public class RecentsAnimationController implements DeathRecipient { 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 3fef87dce460..c8977bede325 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { 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 21e807eee1e8..6fd179550f00 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 @@ class TaskSnapshotPersister { // For snapshots with reduced resolution, do not create or save full sized bitmaps if (mSnapshot.isReducedResolution()) { + swBitmap.recycle(); return true; } @@ -373,6 +375,8 @@ class TaskSnapshotPersister { 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 5410676a49fc..b096bf2d0738 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 class WindowManagerInternal { 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 10ba63e0a9f7..5642b1f91700 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.INPUT_FEATURE_NO_INPUT_CHA 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } @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 @@ public class WindowManagerService extends IWindowManager.Stub } } + @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 @@ public class WindowManagerService extends IWindowManager.Stub + "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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } @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 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { mLastANRState = null; } - mAmInternal.clearSavedANRState(); + mAtmInternal.clearSavedANRState(); } break; case WALLPAPER_DRAW_PENDING_TIMEOUT: { @@ -7201,16 +7227,20 @@ public class WindowManagerService extends IWindowManager.Stub } @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 @@ public class WindowManagerService extends IWindowManager.Stub // 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 @@ public class WindowManagerService extends IWindowManager.Stub } @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 831418b4b2b4..bf2d0df2bec3 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.os.UserHandle; 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 @@ public class WindowManagerShellCommand extends ShellCommand { // 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 @@ public class WindowManagerShellCommand extends ShellCommand { 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 @@ public class WindowManagerShellCommand extends ShellCommand { 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 8276952d8a6c..eacbda198aba 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.LAST_SUB_WINDOW; 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * 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 045f4ebf980f..15a3a1a3c378 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -99,6 +99,7 @@ cc_defaults { "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 c66d03c3a5a3..3943dba7092e 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 @@ public: 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 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO } 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 handleInputChannelDisposed(JNIEnv* env, } 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 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, 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 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, 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 @@ static const JNINativeMethod gInputManagerMethods[] = { { "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 3302dea53506..649f1a56f011 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 4350596ee63e..2dbbf55a9347 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 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { } @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 913b844d8d57..b88165ef0f24 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_NOTIFICATI 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.AudioManager; 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.Log; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 78c0be42a448..e67f8d32fb6d 100644 --- a/services/robotests/Android.mk +++ b/services/robotests/Android.mk @@ -84,7 +84,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ 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_TEST_PACKAGE := FrameworksServicesLib 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 8c0283318419..b83a79fc232c 100644 --- a/services/tests/mockingservicestests/Android.mk +++ b/services/tests/mockingservicestests/Android.mk @@ -22,6 +22,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ frameworks-base-testutils \ services.core \ + services.net \ androidx-test \ mockito-target-extended-minus-junit4 \ platform-test-annotations \ diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index de3d285cd23a..877c8fad3086 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.InstrumentationRegistry; 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.ArgumentCaptor; 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 95ed00f3ad5b..8afd788256c1 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.lightStateToString; 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 static org.mockito.ArgumentMatchers.anyString; 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { private PowerManager.WakeLock mWakeLock; class InjectorForTest extends DeviceIdleController.Injector { + ConnectivityService connectivityService; + LocationManager locationManager; InjectorForTest(Context ctx) { super(ctx); @@ -122,18 +132,19 @@ public class DeviceIdleControllerTest { @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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { } @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 @@ public class DeviceIdleControllerTest { } @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 @@ public class DeviceIdleControllerTest { @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 @@ public class DeviceIdleControllerTest { } @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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { count++; if (count > 10) { - fail(lightStateToString(mDeviceIdleController.getLightState())); + fail("Infinite loop. Check test configuration. Currently at " + + lightStateToString(mDeviceIdleController.getLightState())); } } break; @@ -979,6 +1248,14 @@ public class DeviceIdleControllerTest { 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 index 1752479ade35..b63138edfd21 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 @@ import java.time.ZoneOffset; 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 @@ public class JobStatusTest { 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/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 1eb88baafa48..113ee2df768e 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.NetworkPolicy.WARNING_DISABLED; 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.TextUtils; 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.Period; 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 @@ public class NetworkPolicyManagerServiceTest { 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 43438b99edef..802253280614 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 @@ public class StorageManagerServiceTest { "/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 @@ public class StorageManagerServiceTest { "/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 @@ public class StorageManagerServiceTest { // 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 @@ public class StorageManagerServiceTest { // 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/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java index ea90ffd0792f..0da574239666 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.ACTIVITY_TYPE_STANDARD; 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 @@ public class ActivityDisplayTests extends ActivityTestsBase { 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 c44e492e308b..f42f5b1fff2e 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.Process; 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 @@ import java.util.function.Function; * 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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { 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 ba25b1659bd2..2dfb3751c021 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; + +/** + * 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; - IActivityManager service; - @Override + @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 81a0934a3460..2c993d32c20c 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 @@ package com.android.server.am; 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.assertFalse; 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 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { } /** - * 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 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { } /** + * 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 5fcd2aa35e05..53f67afb629e 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_PRIMAR 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.assertFalse; 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 @@ public class ActivityStackTests extends ActivityTestsBase { } @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 86541b95f395..65e4fa0f4aff 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.Intent; 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 class ActivityStartInterceptorTest { 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 bac4a525b9ea..ba64b51c9e84 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.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; 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.anyObject; 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 @@ public class ActivityStarterTests extends ActivityTestsBase { 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 @@ public class ActivityStarterTests extends ActivityTestsBase { 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 @@ public class ActivityStarterTests extends ActivityTestsBase { .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 @@ public class ActivityStarterTests extends ActivityTestsBase { */ @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 @@ public class ActivityStarterTests extends ActivityTestsBase { @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 @@ public class ActivityStarterTests extends ActivityTestsBase { 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 @@ public class ActivityStarterTests extends ActivityTestsBase { } /** + * 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 2008861907a6..01d51e44146c 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.content.res.Configuration; 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 android.view.DisplayInfo; 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.After; import org.junit.Before; import org.mockito.MockitoAnnotations; +import java.io.File; import java.util.List; /** @@ -97,7 +101,7 @@ public class ActivityTestsBase { new DexmakerShareClassLoaderRule(); private final Context mContext = InstrumentationRegistry.getContext(); - private HandlerThread mHandlerThread; + final TestInjector mTestInjector = new TestInjector(); ActivityTaskManagerService mService; ActivityStackSupervisor mSupervisor; @@ -115,13 +119,12 @@ public class ActivityTestsBase { 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 @@ public class ActivityTestsBase { } 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 @@ public class ActivityTestsBase { 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 @@ public class ActivityTestsBase { 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 @@ public class ActivityTestsBase { // 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 @@ public class ActivityTestsBase { } } + 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 @@ public class ActivityTestsBase { 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 @@ public class ActivityTestsBase { 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 3819e2190d88..06d41f1919c8 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 android.os.Handler; 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 @@ import java.io.File; */ @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 a030210398d6..1b823ff8c6b4 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.util.Log; 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 @@ import java.util.concurrent.TimeUnit; * 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 62c5734561f9..75f7c4c2cdff 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 @@ import java.util.List; @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 fe8256eeb977..719e0edc20b7 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 @@ public class CoreSettingsObserverTest { 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 1276f656f914..27e8c632c1bd 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 @@ public class RecentTasksTest extends ActivityTestsBase { 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 @@ public class RecentTasksTest extends ActivityTestsBase { } 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 0d1302f78316..169204fe374a 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.util.DisplayMetrics.DENSITY_DEFAULT; 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 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { 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 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { 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 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } @Test - public void testNonEmptyLayoutInfersFreeformWithResizeableActivity() { + public void testNonEmptyLayoutUsesFullscreenWithResizeableActivity() { final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() .setWidth(120).setHeight(80).build(); @@ -286,7 +294,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { 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 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } @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 8d54bc236a66..48bfe1d963af 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 @@ public class TaskPersisterTest extends AndroidTestCase { 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 @@ public class TaskPersisterTest extends AndroidTestCase { return taskId; } + @Test public void testTaskIdsPersistence() { SparseBooleanArray taskIdsOnFile = new SparseBooleanArray(); for (int i = 0; i < 100; i++) { @@ -72,21 +81,18 @@ public class TaskPersisterTest extends AndroidTestCase { 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 fa8a09cc2b85..27766d3d8d6c 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 @@ public class TaskRecordTests extends ActivityTestsBase { 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 @@ public class TaskRecordTests extends ActivityTestsBase { } 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 cc4f51987523..75e1d0d800db 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.Message; 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 @@ public class UserControllerTest extends AndroidTestCase { }); } - @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 @@ public class UserControllerTest extends AndroidTestCase { 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 @@ public class UserControllerTest extends AndroidTestCase { // 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 @@ public class UserControllerTest extends AndroidTestCase { 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 @@ public class UserControllerTest extends AndroidTestCase { // 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 @@ public class UserControllerTest extends AndroidTestCase { 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 @@ public class UserControllerTest extends AndroidTestCase { 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 @@ public class UserControllerTest extends AndroidTestCase { 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 @@ public class UserControllerTest extends AndroidTestCase { 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 @@ public class UserControllerTest extends AndroidTestCase { } // 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 @@ public class UserControllerTest extends AndroidTestCase { 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 79eba680c998..92211ec0b649 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 @@ public class TrampolineTest { } @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 c3c07880f605..517b5ade44b8 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.Context; 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 4a33ca37f767..f0ed612400ed 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.hamcrest.CoreMatchers.is; 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 @@ import org.junit.runner.RunWith; public class PackageUserStateTest { @Test - public void testPackageUserState01() { + public void testPackageUserState01() { final PackageUserState testUserState = new PackageUserState(); PackageUserState oldUserState; @@ -84,7 +85,7 @@ public class PackageUserStateTest { } @Test - public void testPackageUserState02() { + public void testPackageUserState02() { final PackageUserState testUserState01 = new PackageUserState(); PackageUserState oldUserState; @@ -102,7 +103,7 @@ public class PackageUserStateTest { } @Test - public void testPackageUserState03() { + public void testPackageUserState03() { final PackageUserState oldUserState = new PackageUserState(); // only new user state has array defined; different @@ -138,7 +139,7 @@ public class PackageUserStateTest { } @Test - public void testPackageUserState04() { + public void testPackageUserState04() { final PackageUserState oldUserState = new PackageUserState(); // only new user state has array defined; different @@ -185,15 +186,19 @@ public class PackageUserStateTest { 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 @@ public class PackageUserStateTest { 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 000000000000..7eccd6728533 --- /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 f115b9cd0fc5..553d234adfca 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.IntentFilter; 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 @@ public class SuspendPackagesTest { } void drainPendingBroadcasts() { - while (pollForIntent(5) != null); + while (pollForIntent(5) != null) ; } Intent receiveIntentFromApp() { @@ -215,15 +216,15 @@ public class SuspendPackagesTest { } 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 @@ public class SuspendPackagesTest { @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 @@ public class SuspendPackagesTest { 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 869f8fa0c2fa..caaa0bbe6c60 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.Matchers.anyInt; 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 @@ public class AppStandbyControllerTests { 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 @@ public class AppStandbyControllerTests { // 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 @@ public class AppStandbyControllerTests { } @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 @@ public class AppStandbyControllerTests { 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 @@ public class AppStandbyControllerTests { // 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 @@ public class AppStandbyControllerTests { } @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 @@ public class AppStandbyControllerTests { // 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 @@ public class AppStandbyControllerTests { @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 @@ public class AppStandbyControllerTests { @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 @@ public class AppStandbyControllerTests { // 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 @@ public class AppStandbyControllerTests { // 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 @@ public class AppStandbyControllerTests { @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 @@ public class AppStandbyControllerTests { @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 class AppStandbyControllerTests { 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 @@ public class AppStandbyControllerTests { @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 @@ public class AppStandbyControllerTests { @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 @@ public class AppStandbyControllerTests { // 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 3dcdd23acf1a..cb8ca7e4bfc6 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 com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; 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 @@ public class DisplayContentTests extends WindowTestsBase { 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); - - // 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)); + dc1.configureDisplayPolicy(); + assertNotNull(dc1.mTapDetector); + // 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 @@ public class DisplayContentTests extends WindowTestsBase { 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; + // sending ACTION_DOWN + final MotionEvent downEvent = MotionEvent.obtain( + downTime, + downTime, + MotionEvent.ACTION_DOWN, + x, + y, + 0 /*metaState*/); + downEvent.setDisplayId(dc.getDisplayId()); + dc.mTapDetector.onPointerEvent(downEvent); - return MotionEvent.obtain( + // sending ACTION_UP + final MotionEvent upEvent = MotionEvent.obtain( downTime, eventTime, - isDownEvent ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP, + MotionEvent.ACTION_UP, x, y, - metaState); + 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 07eafa5c4be4..a028d5eeb02e 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 @@ public class DisplaySettingsTests extends WindowTestsBase { 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 60025f0f03fe..ae40f7e7d235 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 @@ public class ScreenDecorWindowTests { 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 ced084717494..f8e740390291 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 static org.mockito.Mockito.when; 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 @@ import org.junit.runner.RunWith; * 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 474e5b75e5d8..51595514624c 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 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @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 54fd7db9b537..389eba52d88d 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.hardware.display.DisplayManagerInternal; 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.runner.Description; 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 @@ public class WindowManagerServiceRule implements TestRule { 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 @@ public class WindowManagerServiceRule implements TestRule { // 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 @@ public class WindowManagerServiceRule implements TestRule { private void tearDown() { waitUntilWindowManagerHandlersIdle(); + destroyAllSurfaceTransactions(); + destroyAllSurfaceControls(); removeServices(); mService = null; mPolicy = null; @@ -158,4 +185,24 @@ public class WindowManagerServiceRule implements TestRule { 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 a610e6efda8c..3a8c4ae73493 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 @@ public class ZOrderingTests extends WindowTestsBase { 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 @@ public class ZOrderingTests extends WindowTestsBase { } } - class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory { + private class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory { public SurfaceControl.Builder make(SurfaceSession s) { return new HierarchyRecorder(s); } @@ -131,6 +134,7 @@ public class ZOrderingTests extends WindowTestsBase { @After public void after() { mTransaction.close(); + mParentFor.keySet().forEach(SurfaceControl::destroy); mParentFor.clear(); } 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 4e007c2d9929..3266b8b92a19 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index 4e997323a30e..bc54a5d2c499 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 @@ public class AppIdleHistory { 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 class AppIdleHistory { 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 02ad3a8772f8..6a74564367b8 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 @@ public class AppStandbyController { 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 @@ public class AppStandbyController { 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 5bf94afd7683..32fc796e7e05 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 @@ class UsbSerialReader extends IUsbSerialReader.Stub { 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 000000000000..8c9d4dfb0894 --- /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 000000000000..f83a16ec0916 --- /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 4ba44a93f2a8..16dcbe20f46a 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 @@ class IIorapIntegrationTest { 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 c3e91849e636..3681529643bc 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_binary_host { 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 000000000000..7a9f41fd8f38 --- /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 000000000000..d280abce21f5 --- /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 000000000000..0d8b8541caeb --- /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 0ad7e24feb3b..7d791c229a98 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 @@ class ViewCompilerXmlVisitor : public XMLVisitor { private: JavaLangViewBuilder* builder_; }; + } // end namespace int main(int argc, char** argv) { @@ -82,6 +86,11 @@ int main(int argc, char** argv) { 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 @@ int main(int argc, char** argv) { 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 8454d12ac317..d9e71679ced8 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -18,6 +18,7 @@ package android.provider; 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 final class Telephony { 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 @@ public final class Telephony { * 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 final class Telephony { 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 fac1943478b2..b0997f1f24ea 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1202,6 +1202,20 @@ public class CarrierConfigManager { "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 class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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 3ea018af97cf..c83d6aa3897d 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 class PhoneStateListener { */ 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 @@ public class PhoneStateListener { 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 @@ public class PhoneStateListener { } /** + * 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 @@ public class PhoneStateListener { 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 000000000000..00ce03a1f668 --- /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 f85069385f3c..3b4016437e9a 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -88,8 +88,7 @@ public class SubscriptionManager { /** @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 @@ public class SubscriptionManager { /** * 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 @@ public class SubscriptionManager { 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 fca14c823751..69901d297f42 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.IImsConfig; 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 @@ public class TelephonyManager { */ @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 class TelephonyManager { 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 class TelephonyManager { */ 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { /** * 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 @@ public class TelephonyManager { /** * 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 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { } /** {@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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { * 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 @@ public class TelephonyManager { } /** @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 @@ public class TelephonyManager { @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 @@ public class TelephonyManager { } /** - * 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 eb144f9053be..aabefe324d82 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -257,7 +257,7 @@ public class ApnSetting implements Parcelable { 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 @@ public class ApnSetting implements Parcelable { } /** - * 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 @@ public class ApnSetting implements Parcelable { 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 @@ public class ApnSetting implements Parcelable { 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 @@ public class ApnSetting implements Parcelable { 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 @@ public class ApnSetting implements Parcelable { && 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 @@ public class ApnSetting implements Parcelable { && 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 @@ public class ApnSetting implements Parcelable { && 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 e8597b221391..da4822cc1d14 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -68,17 +68,15 @@ public final class DataProfile implements Parcelable { 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 @@ public final class DataProfile implements Parcelable { 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 @@ public final class DataProfile implements Parcelable { 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 final class DataProfile implements Parcelable { public int getMtu() { return mMtu; } /** - * @return The MVNO type: possible values are "imsi", "gid", "spn". - */ - public String getMvnoType() { return mMvnoType; } - - /** - * @return The MVNO match data. For example, - * SPN: A MOBILE, BEN NL, ... - * IMSI: 302720x94, 2060188, ... - * GID: 4E, 33, ... + * @return {@code true} if modem must persist this data profile. */ - public String getMvnoMatchData() { return mMvnoMatchData; } + public boolean isPersistent() { return mPersistent; } /** - * @return True if the data profile was sent to the modem through setDataProfile earlier. + * @return {@code true} if this data profile was used to bring up the last default + * (i.e internet) data connection successfully. */ - public boolean isModemCognitive() { return mModemCognitive; } + public boolean isPreferred() { return mPreferred; } + /** @hide */ @Override public int describeContents() { return 0; @@ -233,11 +224,11 @@ public final class DataProfile implements Parcelable { 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 @@ public final class DataProfile implements Parcelable { return (o == this || toString().equals(o.toString())); } + /** @hide */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mProfileId); @@ -263,11 +255,11 @@ public final class DataProfile implements Parcelable { 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 d6a08543b9cd..bdba8c860db0 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -33,7 +33,7 @@ import java.util.Set; * 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 @@ public final class EmergencyNumber implements Parcelable { } /** - * 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 @@ public final class EmergencyNumber implements Parcelable { } /** - * 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). * - * @return {@code true} if the emergency number is in the specified emergency service - * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise. + * @param categories - the supplied emergency service categories * - * @param categories - emergency service categories {@link EmergencyServiceCategories} + * @return {@code true} if the emergency number is in the specified emergency service + * 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 @@ public final class EmergencyNumber implements Parcelable { } /** - * 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 @@ public final class EmergencyNumber implements Parcelable { 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 8d18ae8ed281..f2d0cbf13cc9 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 @@ public final class ImsExternalCallState implements Parcelable { 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 final class ImsExternalCallState implements Parcelable { 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 @@ public final class ImsExternalCallState implements Parcelable { 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 @@ public final class ImsExternalCallState implements Parcelable { 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 @@ public final class ImsExternalCallState implements Parcelable { 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 @@ public final class ImsExternalCallState implements Parcelable { return mIsPullable; } - public int getCallState() { + public @ExternalCallState int getCallState() { return mCallState; } - public int getCallType() { + public @ExternalCallType int getCallType() { return mCallType; } @@ -166,4 +221,15 @@ public final class ImsExternalCallState implements Parcelable { ", 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 000000000000..c9cf473bb482 --- /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 4f37caa33680..749b1916962e 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.stub.ImsFeatureConfiguration; 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 b77881e29f1e..7f69f43f6cea 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -167,59 +167,6 @@ public abstract class ImsFeature { */ 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 7681aefc07dc..969959433f23 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 @@ public class MmTelFeature extends ImsFeature { * 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 @@ public class MmTelFeature extends ImsFeature { * 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 @@ public class MmTelFeature extends ImsFeature { * 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 @@ public class MmTelFeature extends ImsFeature { * 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 @@ public class MmTelFeature extends ImsFeature { * 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 @@ public class MmTelFeature extends ImsFeature { * {@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 @@ public class MmTelFeature extends ImsFeature { * * @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 @@ public class MmTelFeature extends ImsFeature { * @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 @@ public class MmTelFeature extends ImsFeature { * @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 @@ public class MmTelFeature extends ImsFeature { * @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 @@ public class MmTelFeature extends ImsFeature { * @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 class MmTelFeature extends ImsFeature { * } * } */ - public void setUiTtyMode(int mode, Message onCompleteMessage) { + public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) { // Base Implementation - Should be overridden } @@ -680,7 +705,7 @@ public class MmTelFeature extends ImsFeature { * @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 cecf2e26f139..a08e0313bb5b 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -76,64 +76,6 @@ public class ImsRegistrationImplBase { 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 86cb1b74abd3..b0c875e0c6f2 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -100,6 +100,7 @@ public class DctConstants { 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 38a1bc73c94d..9e42f12d685d 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -53,5 +53,6 @@ oneway interface IPhoneStateListener { 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 000000000000..ede8695ef08e --- /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 32eb12b49cf0..dc233585dea4 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.ServiceState; 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 @@ interface ITelephony { /** * 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 @@ interface ITelephony { */ 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 @@ interface ITelephony { * 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 c03065c34ca8..0baf860efac4 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -80,4 +80,5 @@ interface ITelephonyRegistry { 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 9730ebc57fcf..eda8e7766054 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.Process; 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 @@ public final class TelephonyPermissions { 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 @@ public final class TelephonyPermissions { */ 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 @@ public final class TelephonyPermissions { // 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 3bd3d68ba6cf..2b968aec1496 100644 --- a/test-mock/api/system-current.txt +++ b/test-mock/api/system-current.txt @@ -29,6 +29,7 @@ package android.test.mock { 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 9d260ebf7231..fa5b896ea126 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -774,6 +774,12 @@ public class MockContext extends Context { /** @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 976848c60dc7..eed8ae7c1f70 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 @@ public class AppLaunch extends InstrumentationTestCase { "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 @@ public class AppLaunch extends InstrumentationTestCase { } } 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 @@ public class AppLaunch extends InstrumentationTestCase { // 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 @@ public class AppLaunch extends InstrumentationTestCase { .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 @@ public class AppLaunch extends InstrumentationTestCase { } } + 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 6b5e84e8f9ad..36e7e4182c31 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 98045ae98541..7f06f2cb7aeb 100644 --- a/tests/Internal/src/android/app/WallpaperInfoTest.java +++ b/tests/Internal/src/android/app/WallpaperInfoTest.java @@ -23,6 +23,7 @@ import android.content.Context; 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 @@ public class WallpaperInfoTest { 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 6500428253f6..18cdf96c7131 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 @@ public class Cujs { // 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 03503cec5fec..26146ca0ea6c 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 @@ package com.android.tests.sysmem.host; 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 @@ class Device { /** * 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 b408a81d8f93..b46e642b5e92 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 @@ class Metrics { // 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 03a617c354fa..6174c6ca6190 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -219,7 +219,7 @@ public class ConnectivityManagerTest { // 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 @@ public class ConnectivityManagerTest { // 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 1c77fcc568f6..17bcea05b294 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_ETHERNET; 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.PacketKeepalive; 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.MockableSystemProperties; 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { } @Override + protected Tethering makeTethering() { + return mock(Tethering.class); + } + + @Override protected int reserveNetId() { while (true) { final int netId = super.reserveNetId(); @@ -1023,6 +1036,20 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { RESUMED, LOSING, LOST, - UNAVAILABLE + UNAVAILABLE, + BLOCKED_STATUS } private static class CallbackInfo { @@ -1522,6 +1556,11 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { // - 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 @@ public class ConnectivityServiceTest { // 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 c9987b86cc5c..b165c6bed220 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -2,10 +2,19 @@ LOCAL_PATH := $(call my-dir) 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 a20b9b76a703..b353ff00a23f 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -35,6 +35,43 @@ using ::std::unique_ptr; 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 @@ std::unique_ptr<xml::XmlResource> LoadedApk::LoadXml(const std::string& file_pat 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 84c57c14474a..5b6f45ebb38d 100644 --- a/tools/aapt2/LoadedApk.h +++ b/tools/aapt2/LoadedApk.h @@ -121,8 +121,6 @@ class LoadedApk { 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 000000000000..48c07553ffef --- /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 76a2f2d6eba1..710da40585ac 100755 --- a/tools/hiddenapi/sort_api.sh +++ b/tools/hiddenapi/sort_api.sh @@ -21,4 +21,6 @@ A=( $(uniq <<< "${A[*]}") ) 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 f5cad139bd23..000000000000 --- 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]; - } - }; -} |