diff options
168 files changed, 8585 insertions, 4455 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/api/current.txt b/api/current.txt index 5951433d1b9d..21bab7cf5d1e 100755 --- a/api/current.txt +++ b/api/current.txt @@ -1403,6 +1403,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 +5676,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); @@ -9167,6 +9169,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(); @@ -9194,6 +9197,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); @@ -9202,6 +9206,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); } @@ -9316,6 +9323,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); @@ -15309,20 +15317,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 { @@ -15335,7 +15332,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); } @@ -15351,6 +15348,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); @@ -15468,8 +15485,8 @@ package android.graphics.text { public static class MeasuredText.Builder { ctor public MeasuredText.Builder(char[]); - method public android.graphics.text.MeasuredText.Builder addReplacementRun(android.graphics.Paint, int, int, float); - method public android.graphics.text.MeasuredText.Builder addStyleRun(android.graphics.Paint, int, int, boolean); + 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); @@ -27446,6 +27463,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); @@ -27717,16 +27735,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(); @@ -29722,6 +29740,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 { } @@ -29741,6 +29818,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); @@ -29751,6 +29831,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); @@ -36745,7 +36828,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"; @@ -36867,7 +36950,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"; @@ -36929,15 +37012,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 @@ -36952,7 +37035,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"; @@ -36980,12 +37063,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 @@ -45199,6 +45282,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(); @@ -51350,6 +51434,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(); @@ -51390,6 +51475,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(); diff --git a/api/system-current.txt b/api/system-current.txt index 580a7601ca81..26036ea9c281 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4471,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"; } @@ -5486,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); @@ -6372,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); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 10b7c0b0117d..c2fed66eea41 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -143,6 +143,7 @@ message Atom { BatteryCausedShutdown battery_caused_shutdown = 93; PhoneServiceStateChanged phone_service_state_changed = 94; PhoneStateChanged phone_state_changed = 95; + UserRestrictionChanged user_restriction_changed = 96; } // Pulled events will start at field 10000. @@ -183,7 +184,6 @@ message Atom { DiskIo disk_io = 10032; PowerProfile power_profile = 10033; ProcStats proc_stats_pkg_proc = 10034; - ProcessCpuTime process_cpu_time = 10035; NativeProcessMemoryState native_process_memory_state = 10036; } @@ -2356,10 +2356,10 @@ message NativeProcessMemoryState { optional string process_name = 2; // # of page-faults - optional int64 pgfault = 3; + optional int64 page_fault = 3; // # of major page-faults - optional int64 pgmajfault = 4; + optional int64 page_major_fault = 4; // RSS optional int64 rss_in_bytes = 5; @@ -3035,17 +3035,15 @@ message PowerProfile { } /** - * Pulls process user time and system time. Puller takes a snapshot of all pids - * in the system and returns cpu stats for those that are working at the time. - * Dead pids will be dropped. Kernel processes are excluded. - * Min cool-down is 5 sec. + * Logs when a user restriction was added or removed. + * + * Logged from: + * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java */ -message ProcessCpuTime { - optional int32 uid = 1 [(is_uid) = true]; - - optional string process_name = 2; - // Process cpu time in user space, cumulative from boot/process start - optional int64 user_time_millis = 3; - // Process cpu time in system space, cumulative from boot/process start - optional int64 system_time_millis = 4; -} +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 8871d4d054c5..1a9ba8a8de17 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -229,9 +229,6 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // PowerProfile constants for power model calculations. {android::util::POWER_PROFILE, {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}}, - // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses. - {android::util::PROCESS_CPU_TIME, - {{}, {}, 5 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 857c39045085..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,9 +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/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; @@ -2811,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-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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d00650d40d8f..069effd3ef19 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -250,4 +250,34 @@ public abstract class ActivityManagerInternal { 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/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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index b289a3e59efd..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; @@ -5786,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(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index cf0cad83b41d..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(); 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/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 1fbfa40d5d64..47145874490f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2816,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. @@ -2830,6 +2831,7 @@ public class ConnectivityManager { } onCapabilitiesChanged(network, networkCapabilities); onLinkPropertiesChanged(network, linkProperties); + onBlockedStatusChanged(network, blocked); } /** @@ -2837,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. */ @@ -2916,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; } @@ -2962,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) { @@ -2976,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); } @@ -3022,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: { @@ -3055,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/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/UserManager.java b/core/java/android/os/UserManager.java index 0d5d54704c4f..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. 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/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/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index 0a2d65c9ebff..f9370a8aa6af 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -505,7 +505,7 @@ public class MeasuredParagraph { } mWholeWidth += width; } else { - builder.addReplacementRun(mCachedPaint, start, end, width); + builder.appendReplacementRun(mCachedPaint, end - start, width); } } @@ -520,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. @@ -536,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; 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/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index a401c6def23a..e370e1175de5 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -59,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; @@ -67,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; @@ -540,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) { @@ -567,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; } @@ -597,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); } } @@ -696,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(); } @@ -1386,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; @@ -1400,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!"); @@ -1519,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; @@ -1654,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); } } @@ -1717,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) { @@ -1744,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; } } @@ -1756,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); 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 69d7202c4da8..300bb6fd4890 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -299,7 +299,7 @@ public class WebViewClient { /** * The resource was blocked because it may trick the user into a billing agreement. * - * <p>This constant is only used when targetSdkVersion is greater than {@link + * <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. */ 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/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/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 34e850130b50..29c55c234677 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -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/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/jni/Android.bp b/core/jni/Android.bp index 5e62fd3c33c8..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", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index c05bad2a4332..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); @@ -1367,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), 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/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index cdb65edd81e7..9bedab53bb2c 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. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 95517186fe9e..31212a6ab28f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2915,6 +2915,7 @@ <public name="minimumUiTimeout" /> <public name="isLightTheme" /> <public name="isSplitRequired" /> + <public name="textLocale" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> 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/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/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/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java index 8d38f961f402..16479095a63a 100644 --- a/graphics/java/android/graphics/text/LineBreaker.java +++ b/graphics/java/android/graphics/text/LineBreaker.java @@ -38,11 +38,14 @@ import java.lang.annotation.RetentionPolicy; * <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()) - * .addStyleRun(paint, 0, text.length(), false) // Use paint for whole paragraph. + * .appendStyleRun(paint, 7, false) // Use paint for "Hello, " + * .appednStyleRun(bigPaint, 8, false) // Use bigPaint for "Hello, " * .build(); * * LineBreaker lb = new LineBreaker.Builder() diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java index 852e66e174ca..3efe655b2565 100644 --- a/graphics/java/android/graphics/text/MeasuredText.java +++ b/graphics/java/android/graphics/text/MeasuredText.java @@ -31,6 +31,21 @@ 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( @@ -136,6 +151,19 @@ public class MeasuredText { /** * 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; @@ -143,6 +171,7 @@ public class MeasuredText { private final @NonNull char[] mText; private boolean mComputeHyphenation = false; private boolean mComputeLayout = true; + private int mCurrentOffset = 0; /** * Construct a builder. @@ -159,35 +188,50 @@ public class MeasuredText { } /** - * Apply styles to the given range. + * 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 start an inclusive start index of the range - * @param end an exclusive end index of the range + * @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 addStyleRun(@NonNull Paint paint, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl) { + public Builder appendStyleRun(@NonNull Paint paint, @IntRange(from = 0) int length, + boolean isRtl) { Preconditions.checkNotNull(paint); - nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl); + 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 range is replaced with the object of given + * Used to inform the text layout that the given length is replaced with the object of given * width. * - * Informs the layout engine that the given range should not be processed, instead the + * 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 start an inclusive start index of the range - * @param end an exclusive end index of the 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 addReplacementRun(@NonNull Paint paint, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, - @FloatRange(from = 0) float width) { - nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width); + 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; } @@ -230,9 +274,14 @@ public class 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); 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/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/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/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/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/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/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 bac42ffceb92..0bc54a33347c 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) @@ -5168,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; @@ -5269,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 { @@ -5614,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; @@ -5630,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) { @@ -5640,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, @@ -5649,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 */, @@ -5671,10 +5501,8 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public void onDragCancelled(View v) { mFalsingManager.onNotificatonStopDismissing(); - setSwipingInProgress(false); } /** @@ -5682,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()) { @@ -5701,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; @@ -5709,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. @@ -5743,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()); @@ -5772,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))) { @@ -5786,7 +5607,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onChildSnappedBack(View animView, float targetLeft) { mAmbientState.onDragFinished(animView); updateContinuousShadowDrawing(); @@ -5808,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. @@ -5816,7 +5635,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) public float getFalsingThresholdFactor() { return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; } @@ -5825,5 +5643,197 @@ 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() && (!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 + 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/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/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 980ba8751df4..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) { 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..75077029c16b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -2521,8 +2521,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) { 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/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/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/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b750d7959167..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. @@ -2118,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); @@ -3195,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; } } } @@ -3783,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(); @@ -4893,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(); } @@ -5028,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: { @@ -5045,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); @@ -5600,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/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/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 723531236312..4070bca199e0 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -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); diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index fecb5732745e..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.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 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 android.annotation.Nullable; import android.app.ActivityOptions; @@ -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/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b1aea1fca077..d56b523f07aa 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -26,7 +26,6 @@ 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_NEW_TASK; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; @@ -47,8 +46,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; @@ -67,9 +64,7 @@ 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; @@ -84,7 +79,6 @@ 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; @@ -193,6 +187,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; @@ -262,7 +257,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; @@ -278,7 +272,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.os.storage.StorageManager; -import android.os.storage.StorageManagerInternal; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateUtils; @@ -288,7 +281,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; @@ -324,7 +316,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; @@ -355,7 +346,6 @@ 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; @@ -404,24 +394,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 @@ -455,6 +445,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 @@ -483,7 +479,7 @@ 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 @@ -621,46 +617,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[] { @@ -761,28 +723,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>(); @@ -1098,11 +1038,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. */ @@ -1214,31 +1149,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 { @@ -1426,9 +1336,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final String SERVICE_RECORD_KEY = "servicerecord"; - static ServiceThread sKillThread = null; - static KillHandler sKillHandler = null; - long mLastMemUsageReportTime = 0; /** @@ -1465,30 +1372,6 @@ public class ActivityManagerService extends IActivityManager.Stub return mActivityTaskManager.getGlobalConfiguration(); } - 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); - } - } - } - final class UiHandler extends Handler { public UiHandler() { super(com.android.server.UiThread.get().getLooper(), null, true); @@ -1602,8 +1485,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(); @@ -1616,16 +1499,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: { @@ -1641,19 +1515,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: { @@ -1701,17 +1563,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); - } - } - } + mProcessList.updateAllTimePrefsLocked(msg.arg1); } break; } @@ -1835,17 +1687,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; } @@ -1985,7 +1827,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); @@ -1994,7 +1838,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) { @@ -2262,24 +2106,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; 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; @@ -2312,13 +2170,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); @@ -2468,12 +2320,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()); @@ -2723,329 +2576,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) { @@ -3072,7 +2617,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, @@ -3086,548 +2631,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); @@ -3691,8 +2705,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))) { @@ -3986,9 +3000,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 @@ -4067,7 +3107,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, @@ -4092,32 +3132,19 @@ public class ActivityManagerService extends IActivityManager.Stub }); } - 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) { if (thread == null) { 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--) { @@ -4137,17 +3164,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(); @@ -4158,11 +3175,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; } @@ -4219,7 +3237,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!fromBinderDied) { killProcessQuiet(pid); } - killProcessGroup(app.uid, pid); + ProcessList.killProcessGroup(app.uid, pid); app.killed = true; } @@ -4638,7 +3656,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"); } } @@ -4661,30 +3679,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; @@ -4718,27 +3713,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); @@ -5036,81 +4011,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) { @@ -5231,7 +4131,7 @@ 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)); @@ -5293,128 +4193,10 @@ public class ActivityManagerService extends IActivityManager.Stub 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) { @@ -5432,7 +4214,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. @@ -5488,9 +4270,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; } @@ -5532,7 +4315,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; } @@ -5743,7 +4526,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) { @@ -5754,7 +4537,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; } @@ -5927,7 +4710,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) { @@ -6884,19 +5667,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); } // ========================================================= @@ -7332,7 +6103,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. @@ -7374,6 +6145,7 @@ public class ActivityManagerService extends IActivityManager.Stub ContentProviderRecord cpr; ContentProviderConnection conn = null; ProviderInfo cpi = null; + boolean providerRunning = false; synchronized(this) { long startTime = SystemClock.uptimeMillis(); @@ -7413,8 +6185,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - boolean providerRunning = false; - if (cpr != null && cpr.proc != null) { providerRunning = !cpr.proc.killed; @@ -7492,7 +6262,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"); } } @@ -7739,6 +6509,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) { @@ -7753,13 +6524,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) { @@ -8128,7 +6908,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--) { @@ -8190,9 +6970,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); @@ -8298,67 +7079,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; @@ -8419,6 +7139,7 @@ public class ActivityManagerService extends IActivityManager.Stub abiOverride); } + // TODO: Move to ProcessList? @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, String abiOverride) { @@ -8431,8 +7152,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(); } @@ -8452,7 +7173,7 @@ 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); } @@ -9316,7 +8037,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 { @@ -9466,8 +8187,8 @@ public class ActivityManagerService extends IActivityManager.Stub final long memoryGrowthThreshold = Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD); - for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord proc = mLruProcesses.get(i); + for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord proc = mProcessList.mLruProcesses.get(i); if (proc.notCachedSinceIdle) { if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) { @@ -9597,7 +8318,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"); } } @@ -9984,22 +8705,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); } } @@ -10215,8 +8921,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; } @@ -10249,44 +8955,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.getCurProcState(); - outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk); - outInfo.importanceReasonCode = app.adjTypeCode; - outInfo.processState = app.getCurProcState(); - outInfo.isFocused = (app == getTopAppLocked()); - outInfo.lastActivityTime = app.lastActivityTime; - } - @Override public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() { enforceNotIsolatedCaller("getRunningAppProcesses"); @@ -10294,8 +8962,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); @@ -10304,44 +8970,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 @@ -10388,7 +9019,7 @@ public class ActivityManagerService extends IActivityManager.Stub proc = mPidsSelfLocked.get(Binder.getCallingPid()); } if (proc != null) { - fillInProcMemInfoLocked(proc, outState, clientTargetSdk); + mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk); } } } @@ -11005,6 +9636,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) { @@ -11014,9 +9646,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); @@ -11038,10 +9670,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; } @@ -11094,17 +9726,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; } @@ -11158,11 +9786,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); } @@ -11187,12 +9815,13 @@ public class ActivityManagerService extends IActivityManager.Stub needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep, mTestPssMode, mWakefulness); - if (dumpAll && mPendingStarts.size() > 0) { + 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 (dumpAll) { @@ -11345,9 +9974,9 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(" mLastPowerCheckUptime="); TimeUtils.formatDuration(mLastPowerCheckUptime, pw); pw.println(""); - 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); @@ -11393,9 +10022,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); @@ -11409,8 +10038,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; } @@ -11443,14 +10072,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); } @@ -11461,7 +10093,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); } } } @@ -11475,7 +10108,8 @@ public class ActivityManagerService extends IActivityManager.Stub || !r.pkgList.containsKey(dumpPackage))) { continue; } - it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS); + it.writeToProto(proto, + ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS); } } } @@ -11485,11 +10119,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; } @@ -11623,7 +10258,7 @@ public class ActivityManagerService extends IActivityManager.Stub proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete); proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime); 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); @@ -11704,7 +10339,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:"); @@ -11724,13 +10359,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; } @@ -12365,35 +11003,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, @@ -14088,7 +12700,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); @@ -14228,7 +12840,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) { @@ -14252,9 +12864,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! @@ -14991,15 +13603,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, @@ -15408,8 +14012,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); } @@ -16398,7 +15004,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) { @@ -16430,6 +15036,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 { @@ -17611,10 +16218,10 @@ 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.getCurProcState() == ActivityManager.PROCESS_STATE_NONEXISTENT) { continue; @@ -17790,10 +16397,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; @@ -18343,7 +16950,7 @@ 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 ProcessRecord getTopAppLocked() { + ProcessRecord getTopAppLocked() { final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null; final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null; String pkg; @@ -18417,7 +17024,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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--) { @@ -18478,11 +17085,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); @@ -18557,7 +17164,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--; @@ -18565,7 +17172,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)) { @@ -18576,7 +17183,7 @@ 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); @@ -18669,9 +17276,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!"); } @@ -18681,7 +17288,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(); @@ -18709,7 +17316,7 @@ 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; @@ -18787,7 +17394,7 @@ 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; @@ -19051,7 +17658,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<>(); @@ -19074,8 +17681,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; } @@ -19273,8 +17880,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( @@ -19292,7 +17899,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 */); @@ -19318,8 +17925,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); } @@ -19446,7 +18053,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); @@ -19576,15 +18183,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); } } @@ -19716,8 +18316,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; } @@ -19749,7 +18349,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; } @@ -19810,9 +18410,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); @@ -19831,7 +18432,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"); } } } @@ -20089,8 +18690,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; } @@ -20323,6 +18924,110 @@ public class ActivityManagerService extends IActivityManager.Stub (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) { @@ -20504,8 +19209,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 { @@ -20535,33 +19240,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. @@ -20583,7 +19264,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 9f768a84d223..692b2d433dbc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -481,12 +481,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); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 12ed726eca77..026c5cc3017d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3877,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) { @@ -3902,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 12c6dec091ff..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,6 +58,21 @@ 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.ActivityStack.ActivityState.DESTROYED; +import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; +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.STOPPED; +import static com.android.server.am.ActivityStack.ActivityState.STOPPING; +import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; +import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS; +import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID; +import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT; +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.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IDLE; import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; @@ -76,22 +95,6 @@ import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS 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.ActivityStack.ActivityState.DESTROYED; -import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; -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.STOPPED; -import static com.android.server.am.ActivityStack.ActivityState.STOPPING; -import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; -import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; -import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS; -import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID; -import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT; -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 android.app.ActivityTaskManager.INVALID_TASK_ID; 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; @@ -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; } @@ -1012,7 +1017,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (activity.app == null && app.mUid == activity.info.applicationInfo.uid && processName.equals(activity.processName)) { try { - if (realStartActivityLocked(activity, (ProcessRecord) app.mOwner, + if (realStartActivityLocked(activity, app, top == activity /* andResume */, true /* checkConfig */)) { didSomething = true; } @@ -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.setPendingUiClean(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) { diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index afcf9f98caa2..45a06524745a 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -53,6 +53,11 @@ 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.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; @@ -67,11 +72,6 @@ import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_ 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.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.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; @@ -1277,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 @@ -1295,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 @@ -1460,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; } @@ -1540,7 +1522,7 @@ class ActivityStarter { mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, - preferredLaunchDisplayId, mTargetStack); + mPreferredDisplayId, mTargetStack); return START_SUCCESS; } @@ -1614,7 +1596,6 @@ class ActivityStarter { } else { mPreferredDisplayId = DEFAULT_DISPLAY; } - ensureValidPreferredDisplayId(r); mLaunchMode = r.launchMode; @@ -1707,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/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 2d2701785d03..f79d9aa9ba67 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -4670,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) { @@ -4777,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. @@ -4877,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); } } @@ -5446,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). @@ -5896,6 +5864,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void finishHeavyWeightApp() { synchronized (mGlobalLock) { + if (mHeavyWeightProcess != null) { + mHeavyWeightProcess.finishActivities(); + } ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals( mHeavyWeightProcess); } @@ -6247,20 +6218,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } } - 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(); - - mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, - -1, SYSTEM_UID, UserHandle.USER_ALL); } + // Call into AM outside the synchronized block. + mAmInternal.broadcastCloseSystemDialogs(reason); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 83c4ab50db3a..e8ec0574610c 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -121,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; } @@ -148,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; @@ -181,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; @@ -211,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; @@ -471,7 +471,7 @@ class AppErrors { stopReportingCrashesLocked(r); } if (res == AppErrorDialog.RESTART) { - mService.removeProcessLocked(r, false, true, "crash"); + mService.mProcessList.removeProcessLocked(r, false, true, "crash"); if (taskId != INVALID_TASK_ID) { try { mService.mActivityTaskManager.startActivityFromRecents(taskId, @@ -489,7 +489,7 @@ class AppErrors { // Kill it with fire! mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController()); if (!r.isPersistent()) { - mService.removeProcessLocked(r, false, false, "crash"); + mService.mProcessList.removeProcessLocked(r, false, false, "crash"); mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */); } } finally { @@ -551,7 +551,7 @@ class AppErrors { } else { // Huh. Process.killProcess(pid); - ActivityManagerService.killProcessGroup(uid, pid); + ProcessList.killProcessGroup(uid, pid); } } return true; @@ -714,7 +714,7 @@ 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.mProcessList.removeProcessLocked(app, false, tryAgain, "crash"); mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */); if (!showBackground) { return false; diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index a13cf4d3d2d1..a0977be0e25d 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -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/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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 9e7ce3204444..3f172cc11468 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -16,19 +16,79 @@ 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_DEBUG_TRIAGED_MISSING; +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 com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; - +import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK; +import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT; +import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG; +import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_WITH_WRAPPER; +import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS; +import static com.android.server.am.ActivityManagerService.TAG_LRU; +import static com.android.server.am.ActivityManagerService.TAG_PROCESSES; +import static com.android.server.am.ActivityManagerService.TAG_PSS; +import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS; + +import 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; import android.app.ActivityManager; +import android.app.AppGlobals; import android.app.AppProtoEnums; +import android.app.IApplicationThread; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.net.Uri; +import android.os.Binder; import android.os.Build; +import android.os.Bundle; +import android.os.FactoryTest; +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 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 android.content.res.Resources; @@ -36,7 +96,15 @@ import android.graphics.Point; import android.os.SystemProperties; import android.net.LocalSocketAddress; import android.net.LocalSocket; +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; /** @@ -47,7 +115,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 +197,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 +216,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; @@ -168,6 +236,12 @@ public final class ProcessList { 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 // can't give it a different value for every possible kind of process. @@ -199,6 +273,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(); @@ -206,6 +397,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(); @@ -221,12 +422,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 @@ -246,27 +447,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; @@ -277,13 +478,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; @@ -297,10 +500,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]); } @@ -320,7 +523,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) { @@ -477,7 +680,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(' '); } @@ -737,12 +940,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; } /** @@ -858,4 +1061,1515 @@ public final class ProcessList { } } } + + 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, 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); + 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 */, abiOverride); + } + + @GuardedBy("mService") + final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, + boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, + boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge, + String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) { + long startTime = SystemClock.elapsedRealtime(); + ProcessRecord app; + if (!isolated) { + app = getProcessRecordLocked(processName, info.uid, keepIfLarge); + checkSlow(startTime, "startProcess: after getProcessRecord"); + + if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) { + // If we are in the background, then check to see if this process + // is bad. If so, we will just silently fail. + if (mService.mAppErrors.isBadProcessLocked(info)) { + if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + + "/" + info.processName); + return null; + } + } else { + // When the user is explicitly starting a process, then clear its + // crash count so that we won't make it bad until they see at + // least one crash dialog again, and make the process good again + // if it had been bad. + if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + + "/" + info.processName); + mService.mAppErrors.resetProcessCrashTimeLocked(info); + if (mService.mAppErrors.isBadProcessLocked(info)) { + EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, + UserHandle.getUserId(info.uid), info.uid, + info.processName); + mService.mAppErrors.clearBadProcessLocked(info); + if (app != null) { + app.bad = false; + } + } + } + } else { + // If this is an isolated process, it can't re-use an existing process. + app = null; + } + + // We don't have to do anything more if: + // (1) There is an existing application record; and + // (2) The caller doesn't think it is dead, OR there is no thread + // object attached to it so we know it couldn't have crashed; and + // (3) There is a pid assigned to it, so it is either starting or + // already running. + if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName + + " app=" + app + " knownToBeDead=" + knownToBeDead + + " thread=" + (app != null ? app.thread : null) + + " pid=" + (app != null ? app.pid : -1)); + if (app != null && app.pid > 0) { + if ((!knownToBeDead && !app.killed) || app.thread == null) { + // We already have the app running, or are waiting for it to + // come up (we have a pid but not yet its thread), so keep it. + if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app); + // If this is a new package in the process, add the package to the list + app.addPackage(info.packageName, info.versionCode, mService.mProcessStats); + checkSlow(startTime, "startProcess: done, added package to proc"); + return app; + } + + // An application record is attached to a previous process, + // clean it up now. + if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app); + checkSlow(startTime, "startProcess: bad proc running, killing"); + ProcessList.killProcessGroup(app.uid, app.pid); + mService.handleAppDiedLocked(app, true, true); + checkSlow(startTime, "startProcess: done killing old proc"); + } + + String hostingNameStr = hostingName != null + ? hostingName.flattenToShortString() : null; + + if (app == null) { + checkSlow(startTime, "startProcess: creating new process record"); + app = newProcessRecordLocked(info, processName, isolated, isolatedUid); + if (app == null) { + Slog.w(TAG, "Failed making new process record for " + + processName + "/" + info.uid + " isolated=" + isolated); + return null; + } + app.crashHandler = crashHandler; + app.isolatedEntryPoint = entryPoint; + app.isolatedEntryPointArgs = entryPointArgs; + checkSlow(startTime, "startProcess: done creating new process record"); + } else { + // If this is a new package in the process, add the package to the list + app.addPackage(info.packageName, info.versionCode, mService.mProcessStats); + checkSlow(startTime, "startProcess: added package to existing proc"); + } + + // If the system is not ready yet, then hold off on starting this + // process until it is. + if (!mService.mProcessesReady + && !mService.isAllowedWhileBooting(info) + && !allowWhileBooting) { + if (!mService.mProcessesOnHold.contains(app)) { + mService.mProcessesOnHold.add(app); + } + if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, + "System not ready, putting on hold: " + app); + checkSlow(startTime, "startProcess: returning with proc on hold"); + return app; + } + + checkSlow(startTime, "startProcess: stepping in to startProcess"); + final boolean success = startProcessLocked(app, hostingType, hostingNameStr, + abiOverride); + checkSlow(startTime, "startProcess: done starting proc!"); + return success ? app : null; + } + + @GuardedBy("mService") + private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) { + StringBuilder sb = null; + if (app.killedByAm) { + if (sb == null) sb = new StringBuilder(); + sb.append("killedByAm=true;"); + } + if (mProcessNames.get(app.processName, app.uid) != app) { + if (sb == null) sb = new StringBuilder(); + sb.append("No entry in mProcessNames;"); + } + if (!app.pendingStart) { + if (sb == null) sb = new StringBuilder(); + sb.append("pendingStart=false;"); + } + if (app.startSeq > expectedStartSeq) { + if (sb == null) sb = new StringBuilder(); + sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";"); + } + return sb == null ? null : sb.toString(); + } + + @GuardedBy("mService") + private boolean handleProcessStartedLocked(ProcessRecord pending, + Process.ProcessStartResult startResult, long expectedStartSeq) { + // Indicates that this process start has been taken care of. + if (mPendingStarts.get(expectedStartSeq) == null) { + if (pending.pid == startResult.pid) { + pending.setUsingWrapper(startResult.usingWrapper); + // TODO: Update already existing clients of usingWrapper + } + return false; + } + return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper, + expectedStartSeq, false); + } + + @GuardedBy("mService") + boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper, + long expectedStartSeq, boolean procAttached) { + mPendingStarts.remove(expectedStartSeq); + final String reason = isProcStartValidLocked(app, expectedStartSeq); + if (reason != null) { + Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + + pid + + ", " + reason); + app.pendingStart = false; + killProcessQuiet(pid); + Process.killProcessGroup(app.uid, app.pid); + return false; + } + mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid); + checkSlow(app.startTime, "startProcess: done updating battery stats"); + + EventLog.writeEvent(EventLogTags.AM_PROC_START, + UserHandle.getUserId(app.startUid), pid, app.startUid, + app.processName, app.hostingType, + app.hostingNameStr != null ? app.hostingNameStr : ""); + + try { + AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid, + app.seInfo, app.info.sourceDir, pid); + } catch (RemoteException ex) { + // Ignore + } + + if (app.isPersistent()) { + Watchdog.getInstance().processStarted(app.processName, pid); + } + + checkSlow(app.startTime, "startProcess: building log message"); + StringBuilder buf = mStringBuilder; + buf.setLength(0); + buf.append("Start proc "); + buf.append(pid); + buf.append(':'); + buf.append(app.processName); + buf.append('/'); + UserHandle.formatUid(buf, app.startUid); + if (app.isolatedEntryPoint != null) { + buf.append(" ["); + buf.append(app.isolatedEntryPoint); + buf.append("]"); + } + buf.append(" for "); + buf.append(app.hostingType); + if (app.hostingNameStr != null) { + buf.append(" "); + buf.append(app.hostingNameStr); + } + mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid); + app.setPid(pid); + app.setUsingWrapper(usingWrapper); + app.pendingStart = false; + checkSlow(app.startTime, "startProcess: starting to update pids map"); + ProcessRecord oldApp; + synchronized (mService.mPidsSelfLocked) { + oldApp = mService.mPidsSelfLocked.get(pid); + } + // If there is already an app occupying that pid that hasn't been cleaned up + if (oldApp != null && !app.isolated) { + // Clean up anything relating to this pid first + Slog.w(TAG, "Reusing pid " + pid + + " while app is still mapped to it"); + mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1, + true /*replacingPid*/); + } + synchronized (mService.mPidsSelfLocked) { + mService.mPidsSelfLocked.put(pid, app); + if (!procAttached) { + Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + msg.obj = app; + mService.mHandler.sendMessageDelayed(msg, usingWrapper + ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); + } + } + checkSlow(app.startTime, "startProcess: done updating pids map"); + return true; + } + + final void removeLruProcessLocked(ProcessRecord app) { + int lrui = mLruProcesses.lastIndexOf(app); + if (lrui >= 0) { + if (!app.killed) { + if (app.isPersistent()) { + Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app); + } else { + Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app); + if (app.pid > 0) { + killProcessQuiet(app.pid); + ProcessList.killProcessGroup(app.uid, app.pid); + } else { + app.pendingStart = false; + } + } + } + if (lrui <= mLruProcessActivityStart) { + mLruProcessActivityStart--; + } + if (lrui <= mLruProcessServiceStart) { + mLruProcessServiceStart--; + } + mLruProcesses.remove(lrui); + } + } + + void killAllBackgroundProcessesLocked() { + final ArrayList<ProcessRecord> procs = new ArrayList<>(); + final int NP = mProcessNames.getMap().size(); + for (int ip = 0; ip < NP; ip++) { + final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); + final int NA = apps.size(); + for (int ia = 0; ia < NA; ia++) { + final ProcessRecord app = apps.valueAt(ia); + if (app.isPersistent()) { + // We don't kill persistent processes. + continue; + } + if (app.removed) { + procs.add(app); + } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + app.removed = true; + procs.add(app); + } + } + } + + final int N = procs.size(); + for (int i = 0; i < N; i++) { + removeProcessLocked(procs.get(i), false, true, "kill all background"); + } + } + + @GuardedBy("mService") + final boolean killPackageProcessesLocked(String packageName, int appId, + int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart, + boolean doit, boolean evenPersistent, String reason) { + ArrayList<ProcessRecord> procs = new ArrayList<>(); + + // Remove all processes this package may have touched: all with the + // same UID (except for the system or root user), and all whose name + // matches the package name. + final int NP = mProcessNames.getMap().size(); + for (int ip = 0; ip < NP; ip++) { + SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); + final int NA = apps.size(); + for (int ia = 0; ia < NA; ia++) { + ProcessRecord app = apps.valueAt(ia); + if (app.isPersistent() && !evenPersistent) { + // we don't kill persistent processes + continue; + } + if (app.removed) { + if (doit) { + procs.add(app); + } + continue; + } + + // Skip process if it doesn't meet our oom adj requirement. + if (app.setAdj < minOomAdj) { + continue; + } + + // If no package is specified, we call all processes under the + // give user id. + if (packageName == null) { + if (userId != UserHandle.USER_ALL && app.userId != userId) { + continue; + } + if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) { + continue; + } + // Package has been specified, we want to hit all processes + // that match it. We need to qualify this by the processes + // that are running under the specified app and user ID. + } else { + final boolean isDep = app.pkgDeps != null + && app.pkgDeps.contains(packageName); + if (!isDep && UserHandle.getAppId(app.uid) != appId) { + continue; + } + if (userId != UserHandle.USER_ALL && app.userId != userId) { + continue; + } + if (!app.pkgList.containsKey(packageName) && !isDep) { + continue; + } + } + + // Process has passed all conditions, kill it! + if (!doit) { + return true; + } + app.removed = true; + procs.add(app); + } + } + + int N = procs.size(); + for (int i=0; i<N; i++) { + removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); + } + mService.updateOomAdjLocked(); + return N > 0; + } + @GuardedBy("mService") + boolean removeProcessLocked(ProcessRecord app, + boolean callerWillRestart, boolean allowRestart, String reason) { + final String name = app.processName; + final int uid = app.uid; + if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES, + "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")"); + + ProcessRecord old = mProcessNames.get(name, uid); + if (old != app) { + // This process is no longer active, so nothing to do. + Slog.w(TAG, "Ignoring remove of inactive process: " + app); + return false; + } + removeProcessNameLocked(name, uid); + mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController()); + + boolean needRestart = false; + if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app + .pendingStart)) { + int pid = app.pid; + if (pid > 0) { + synchronized (mService.mPidsSelfLocked) { + mService.mPidsSelfLocked.remove(pid); + mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); + } + mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); + if (app.isolated) { + mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid); + mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid); + } + } + boolean willRestart = false; + if (app.isPersistent() && !app.isolated) { + if (!callerWillRestart) { + willRestart = true; + } else { + needRestart = true; + } + } + app.kill(reason, true); + mService.handleAppDiedLocked(app, willRestart, allowRestart); + if (willRestart) { + removeLruProcessLocked(app); + mService.addAppLocked(app.info, null, false, null /* ABI override */); + } + } else { + mRemovedProcesses.add(app); + } + + return needRestart; + } + + @GuardedBy("mService") + final void addProcessNameLocked(ProcessRecord proc) { + // We shouldn't already have a process under this name, but just in case we + // need to clean up whatever may be there now. + ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid); + if (old == proc && proc.isPersistent()) { + // We are re-adding a persistent process. Whatevs! Just leave it there. + Slog.w(TAG, "Re-adding persistent process " + proc); + } else if (old != null) { + Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc); + } + UidRecord uidRec = mService.mActiveUids.get(proc.uid); + if (uidRec == null) { + uidRec = new UidRecord(proc.uid); + // This is the first appearance of the uid, report it now! + if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, + "Creating new process uid: " + uidRec); + if (Arrays.binarySearch(mService.mDeviceIdleTempWhitelist, + UserHandle.getAppId(proc.uid)) >= 0 + || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) { + uidRec.setWhitelist = uidRec.curWhitelist = true; + } + uidRec.updateHasInternetPermission(); + mService.mActiveUids.put(proc.uid, uidRec); + EventLogTags.writeAmUidRunning(uidRec.uid); + mService.noteUidProcessState(uidRec.uid, uidRec.curProcState); + } + proc.uidRecord = uidRec; + + // Reset render thread tid if it was already set, so new process can set it again. + proc.renderThreadTid = 0; + uidRec.numProcs++; + mProcessNames.put(proc.processName, proc.uid, proc); + if (proc.isolated) { + mIsolatedProcesses.put(proc.uid, proc); + } + } + + @GuardedBy("mService") + final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, + boolean isolated, int isolatedUid) { + String proc = customProcess != null ? customProcess : info.processName; + final int userId = UserHandle.getUserId(info.uid); + int uid = info.uid; + if (isolated) { + if (isolatedUid == 0) { + int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1; + while (true) { + if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID + || mNextIsolatedProcessUid > LAST_ISOLATED_UID) { + mNextIsolatedProcessUid = FIRST_ISOLATED_UID; + } + uid = UserHandle.getUid(userId, mNextIsolatedProcessUid); + mNextIsolatedProcessUid++; + if (mIsolatedProcesses.indexOfKey(uid) < 0) { + // No process for this uid, use it. + break; + } + stepsLeft--; + if (stepsLeft <= 0) { + return null; + } + } + } else { + // Special case for startIsolatedProcess (internal only), where + // the uid of the isolated process is specified by the caller. + uid = isolatedUid; + } + mService.getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid); + + // Register the isolated UID with this application so BatteryStats knows to + // attribute resource usage to the application. + // + // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats + // about the process state of the isolated UID *before* it is registered with the + // owning application. + mService.mBatteryStatsService.addIsolatedUid(uid, info.uid); + StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid, + StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED); + } + final ProcessRecord r = new ProcessRecord(mService, info, proc, uid, + mService.getGlobalConfiguration()); + + if (!mService.mBooted && !mService.mBooting + && userId == UserHandle.USER_SYSTEM + && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) { + // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc. + r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT); + r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT; + r.setPersistent(true); + r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ; + } + if (isolated && isolatedUid != 0) { + // Special case for startIsolatedProcess (internal only) - assume the process + // is required by the system server to prevent it being killed. + r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ; + } + addProcessNameLocked(r); + return r; + } + + @GuardedBy("mService") + final ProcessRecord removeProcessNameLocked(final String name, final int uid) { + return removeProcessNameLocked(name, uid, null); + } + + @GuardedBy("mService") + final ProcessRecord removeProcessNameLocked(final String name, final int uid, + final ProcessRecord expecting) { + ProcessRecord old = mProcessNames.get(name, uid); + // Only actually remove when the currently recorded value matches the + // record that we expected; if it doesn't match then we raced with a + // newly created process and we don't want to destroy the new one. + if ((expecting == null) || (old == expecting)) { + mProcessNames.remove(name, uid); + } + if (old != null && old.uidRecord != null) { + old.uidRecord.numProcs--; + if (old.uidRecord.numProcs == 0) { + // No more processes using this uid, tell clients it is gone. + if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, + "No more processes in " + old.uidRecord); + mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE); + EventLogTags.writeAmUidStopped(uid); + mService.mActiveUids.remove(uid); + mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT); + } + old.uidRecord = null; + } + mIsolatedProcesses.remove(uid); + return old; + } + + /** Call setCoreSettings on all LRU processes, with the new settings. */ + @GuardedBy("mService") + void updateCoreSettingsLocked(Bundle settings) { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord processRecord = mLruProcesses.get(i); + try { + if (processRecord.thread != null) { + processRecord.thread.setCoreSettings(settings); + } + } catch (RemoteException re) { + /* ignore */ + } + } + } + + /** + * Kill all background processes except for ones with targetSdk lower than minTargetSdk and + * procstate lower than maxProcState. + * @param minTargetSdk + * @param maxProcState + */ + @GuardedBy("mService") + void killAllBackgroundProcessesExceptLocked(int minTargetSdk, int maxProcState) { + final ArrayList<ProcessRecord> procs = new ArrayList<>(); + final int NP = mProcessNames.getMap().size(); + for (int ip = 0; ip < NP; ip++) { + final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); + final int NA = apps.size(); + for (int ia = 0; ia < NA; ia++) { + final ProcessRecord app = apps.valueAt(ia); + if (app.removed) { + procs.add(app); + } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk) + && (maxProcState < 0 || app.setProcState > maxProcState)) { + app.removed = true; + procs.add(app); + } + } + } + + final int N = procs.size(); + for (int i = 0; i < N; i++) { + removeProcessLocked(procs.get(i), false, true, "kill all background except"); + } + } + + /** + * Call updateTimePrefs on all LRU processes + * @param timePref The time pref to pass to each process + */ + @GuardedBy("mService") + void updateAllTimePrefsLocked(int timePref) { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mLruProcesses.get(i); + if (r.thread != null) { + try { + r.thread.updateTimePrefs(timePref); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to update preferences for: " + + r.info.processName); + } + } + } + } + + @GuardedBy("mService") + void setAllHttpProxyLocked(String host, String port, String exclList, Uri pacFileUrl) { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mLruProcesses.get(i); + // Don't dispatch to isolated processes as they can't access + // ConnectivityManager and don't have network privileges anyway. + if (r.thread != null && !r.isolated) { + try { + r.thread.setHttpProxy(host, port, exclList, pacFileUrl); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to update http proxy for: " + + r.info.processName); + } + } + } + } + + @GuardedBy("mService") + void clearAllDnsCacheLocked() { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mLruProcesses.get(i); + if (r.thread != null) { + try { + r.thread.clearDnsCache(); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName); + } + } + } + } + + @GuardedBy("mService") + void handleAllTrustStorageUpdateLocked() { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mLruProcesses.get(i); + if (r.thread != null) { + try { + r.thread.handleTrustStorageUpdate(); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to handle trust storage update for: " + + r.info.processName); + } + } + } + } + + @GuardedBy("mService") + int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, + String what, Object obj, ProcessRecord srcApp) { + app.lastActivityTime = now; + + if (app.hasActivitiesOrRecentTasks()) { + // Don't want to touch dependent processes that are hosting activities. + return index; + } + + int lrui = mLruProcesses.lastIndexOf(app); + if (lrui < 0) { + Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: " + + what + " " + obj + " from " + srcApp); + return index; + } + + if (lrui >= index) { + // Don't want to cause this to move dependent processes *back* in the + // list as if they were less frequently used. + return index; + } + + if (lrui >= mLruProcessActivityStart) { + // Don't want to touch dependent processes that are hosting activities. + return index; + } + + mLruProcesses.remove(lrui); + if (index > 0) { + index--; + } + if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index + + " in LRU list: " + app); + mLruProcesses.add(index, app); + return index; + } + + final void updateLruProcessLocked(ProcessRecord app, boolean activityChange, + ProcessRecord client) { + final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities() + || app.treatLikeActivity; + final boolean hasService = false; // not impl yet. app.services.size() > 0; + if (!activityChange && hasActivity) { + // The process has activities, so we are only allowing activity-based adjustments + // to move it. It should be kept in the front of the list with other + // processes that have activities, and we don't want those to change their + // order except due to activity operations. + return; + } + + mLruSeq++; + final long now = SystemClock.uptimeMillis(); + app.lastActivityTime = now; + + // First a quick reject: if the app is already at the position we will + // put it, then there is nothing to do. + if (hasActivity) { + final int N = mLruProcesses.size(); + if (N > 0 && mLruProcesses.get(N - 1) == app) { + if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app); + return; + } + } else { + if (mLruProcessServiceStart > 0 + && mLruProcesses.get(mLruProcessServiceStart-1) == app) { + if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app); + return; + } + } + + int lrui = mLruProcesses.lastIndexOf(app); + + if (app.isPersistent() && lrui >= 0) { + // We don't care about the position of persistent processes, as long as + // they are in the list. + if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app); + return; + } + + /* In progress: compute new position first, so we can avoid doing work + if the process is not actually going to move. Not yet working. + int addIndex; + int nextIndex; + boolean inActivity = false, inService = false; + if (hasActivity) { + // Process has activities, put it at the very tipsy-top. + addIndex = mLruProcesses.size(); + nextIndex = mLruProcessServiceStart; + inActivity = true; + } else if (hasService) { + // Process has services, put it at the top of the service list. + addIndex = mLruProcessActivityStart; + nextIndex = mLruProcessServiceStart; + inActivity = true; + inService = true; + } else { + // Process not otherwise of interest, it goes to the top of the non-service area. + addIndex = mLruProcessServiceStart; + if (client != null) { + int clientIndex = mLruProcesses.lastIndexOf(client); + if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating " + + app); + if (clientIndex >= 0 && addIndex > clientIndex) { + addIndex = clientIndex; + } + } + nextIndex = addIndex > 0 ? addIndex-1 : addIndex; + } + + Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act=" + + mLruProcessActivityStart + "): " + app); + */ + + if (lrui >= 0) { + if (lrui < mLruProcessActivityStart) { + mLruProcessActivityStart--; + } + if (lrui < mLruProcessServiceStart) { + mLruProcessServiceStart--; + } + /* + if (addIndex > lrui) { + addIndex--; + } + if (nextIndex > lrui) { + nextIndex--; + } + */ + mLruProcesses.remove(lrui); + } + + /* + mLruProcesses.add(addIndex, app); + if (inActivity) { + mLruProcessActivityStart++; + } + if (inService) { + mLruProcessActivityStart++; + } + */ + + int nextIndex; + if (hasActivity) { + final int N = mLruProcesses.size(); + if ((!app.hasActivities() || app.hasRecentTasks()) + && mLruProcessActivityStart < (N - 1)) { + // Process doesn't have activities, but has clients with + // activities... move it up, but one below the top (the top + // should always have a real activity). + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Adding to second-top of LRU activity list: " + app); + mLruProcesses.add(N - 1, app); + // To keep it from spamming the LRU list (by making a bunch of clients), + // we will push down any other entries owned by the app. + final int uid = app.info.uid; + for (int i = N - 2; i > mLruProcessActivityStart; i--) { + ProcessRecord subProc = mLruProcesses.get(i); + if (subProc.info.uid == uid) { + // We want to push this one down the list. If the process after + // it is for the same uid, however, don't do so, because we don't + // want them internally to be re-ordered. + if (mLruProcesses.get(i - 1).info.uid != uid) { + if (DEBUG_LRU) Slog.d(TAG_LRU, + "Pushing uid " + uid + " swapping at " + i + ": " + + mLruProcesses.get(i) + " : " + + mLruProcesses.get(i - 1)); + ProcessRecord tmp = mLruProcesses.get(i); + mLruProcesses.set(i, mLruProcesses.get(i - 1)); + mLruProcesses.set(i - 1, tmp); + i--; + } + } else { + // A gap, we can stop here. + break; + } + } + } else { + // Process has activities, put it at the very tipsy-top. + if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app); + mLruProcesses.add(app); + } + nextIndex = mLruProcessServiceStart; + } else if (hasService) { + // Process has services, put it at the top of the service list. + if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app); + mLruProcesses.add(mLruProcessActivityStart, app); + nextIndex = mLruProcessServiceStart; + mLruProcessActivityStart++; + } else { + // Process not otherwise of interest, it goes to the top of the non-service area. + int index = mLruProcessServiceStart; + if (client != null) { + // If there is a client, don't allow the process to be moved up higher + // in the list than that client. + int clientIndex = mLruProcesses.lastIndexOf(client); + if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client + + " when updating " + app); + if (clientIndex <= lrui) { + // Don't allow the client index restriction to push it down farther in the + // list than it already is. + clientIndex = lrui; + } + if (clientIndex >= 0 && index > clientIndex) { + index = clientIndex; + } + } + if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app); + mLruProcesses.add(index, app); + nextIndex = index - 1; + mLruProcessActivityStart++; + mLruProcessServiceStart++; + } + + // If the app is currently using a content provider or service, + // bump those processes as well. + for (int j = app.connections.size() - 1; j >= 0; j--) { + ConnectionRecord cr = app.connections.valueAt(j); + if (cr.binding != null && !cr.serviceDead && cr.binding.service != null + && cr.binding.service.app != null + && cr.binding.service.app.lruSeq != mLruSeq + && !cr.binding.service.app.isPersistent()) { + nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, + now, + nextIndex, + "service connection", cr, app); + } + } + for (int j = app.conProviders.size() - 1; j >= 0; j--) { + ContentProviderRecord cpr = app.conProviders.get(j).provider; + if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) { + nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, + "provider reference", cpr, app); + } + } + } + + final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) { + final IBinder threadBinder = thread.asBinder(); + // Find the application record. + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord rec = mLruProcesses.get(i); + if (rec.thread != null && rec.thread.asBinder() == threadBinder) { + return rec; + } + } + return null; + } + + boolean haveBackgroundProcessLocked() { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord rec = mLruProcesses.get(i); + if (rec.thread != null + && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) { + return true; + } + } + return false; + } + + private static int procStateToImportance(int procState, int memAdj, + ActivityManager.RunningAppProcessInfo currApp, + int clientTargetSdk) { + int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk( + procState, clientTargetSdk); + if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { + currApp.lru = memAdj; + } else { + currApp.lru = 0; + } + return imp; + } + + @GuardedBy("mService") + void fillInProcMemInfoLocked(ProcessRecord app, + ActivityManager.RunningAppProcessInfo outInfo, + int clientTargetSdk) { + outInfo.pid = app.pid; + outInfo.uid = app.info.uid; + if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) { + outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE; + } + if (app.isPersistent()) { + outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT; + } + if (app.hasActivities()) { + outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES; + } + outInfo.lastTrimLevel = app.trimMemoryLevel; + int adj = app.curAdj; + int procState = app.getCurProcState(); + outInfo.importance = procStateToImportance(procState, adj, outInfo, + clientTargetSdk); + outInfo.importanceReasonCode = app.adjTypeCode; + outInfo.processState = app.getCurProcState(); + outInfo.isFocused = (app == mService.getTopAppLocked()); + outInfo.lastActivityTime = app.lastActivityTime; + } + + @GuardedBy("mService") + List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers, + int userId, boolean allUids, int callingUid, int clientTargetSdk) { + // Lazy instantiation of list + List<ActivityManager.RunningAppProcessInfo> runList = null; + + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if ((!allUsers && app.userId != userId) + || (!allUids && app.uid != callingUid)) { + continue; + } + if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) { + // Generate process state info for running application + ActivityManager.RunningAppProcessInfo currApp = + new ActivityManager.RunningAppProcessInfo(app.processName, + app.pid, app.getPackageList()); + fillInProcMemInfoLocked(app, currApp, clientTargetSdk); + if (app.adjSource instanceof ProcessRecord) { + currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid; + currApp.importanceReasonImportance = + ActivityManager.RunningAppProcessInfo.procStateToImportance( + app.adjSourceProcState); + } else if (app.adjSource instanceof ActivityRecord) { + ActivityRecord r = (ActivityRecord)app.adjSource; + if (r.app != null) currApp.importanceReasonPid = r.app.getPid(); + } + if (app.adjTarget instanceof ComponentName) { + currApp.importanceReasonComponent = (ComponentName)app.adjTarget; + } + //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance + // + " lru=" + currApp.lru); + if (runList == null) { + runList = new ArrayList<>(); + } + runList.add(currApp); + } + } + return runList; + } + + @GuardedBy("mService") + int getLruSizeLocked() { + return mLruProcesses.size(); + } + + @GuardedBy("mService") + void dumpLruListHeaderLocked(PrintWriter pw) { + pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size()); + pw.print(" total, non-act at "); + pw.print(mLruProcesses.size() - mLruProcessActivityStart); + pw.print(", non-svc at "); + pw.print(mLruProcesses.size() - mLruProcessServiceStart); + pw.println("):"); + } + + @GuardedBy("mService") + ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) { + ArrayList<ProcessRecord> procs; + if (args != null && args.length > start + && args[start].charAt(0) != '-') { + procs = new ArrayList<ProcessRecord>(); + int pid = -1; + try { + pid = Integer.parseInt(args[start]); + } catch (NumberFormatException e) { + } + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord proc = mLruProcesses.get(i); + if (proc.pid > 0 && proc.pid == pid) { + procs.add(proc); + } else if (allPkgs && proc.pkgList != null + && proc.pkgList.containsKey(args[start])) { + procs.add(proc); + } else if (proc.processName.equals(args[start])) { + procs.add(proc); + } + } + if (procs.size() <= 0) { + return null; + } + } else { + procs = new ArrayList<ProcessRecord>(mLruProcesses); + } + return procs; + } + + @GuardedBy("mService") + void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId, + boolean updateFrameworkRes) { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord app = mLruProcesses.get(i); + if (app.thread == null) { + continue; + } + + if (userId != UserHandle.USER_ALL && app.userId != userId) { + continue; + } + + final int packageCount = app.pkgList.size(); + for (int j = 0; j < packageCount; j++) { + final String packageName = app.pkgList.keyAt(j); + if (updateFrameworkRes || packagesToUpdate.contains(packageName)) { + try { + final ApplicationInfo ai = AppGlobals.getPackageManager() + .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); + if (ai != null) { + app.thread.scheduleApplicationInfoChanged(ai); + } + } catch (RemoteException e) { + Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s", + packageName, app)); + } + } + } + } + } + + @GuardedBy("mService") + void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) { + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mLruProcesses.get(i); + if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) { + try { + r.thread.dispatchPackageBroadcast(cmd, packages); + } catch (RemoteException ex) { + } + } + } + } } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 0eb535b74d13..fa7a08bb12eb 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -28,6 +28,7 @@ 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; @@ -60,6 +61,7 @@ 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; @@ -733,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; } @@ -836,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 */ @@ -1157,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(); @@ -1176,10 +1185,55 @@ 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(); } @@ -1256,8 +1310,8 @@ final class ProcessRecord implements WindowProcessListener { if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID); - for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) { - ProcessRecord r = mService.mLruProcesses.get(i); + 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) { diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java index 792b66be6dd9..1743ddeedd91 100644 --- a/services/core/java/com/android/server/am/WindowProcessController.java +++ b/services/core/java/com/android/server/am/WindowProcessController.java @@ -37,6 +37,7 @@ import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_N 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; @@ -674,6 +675,35 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mAtm.mH.sendMessage(m); } + void clearWaitingToKill() { + if (mListener == null) return; + // Posting on handler so WM lock isn't held when we call into AM. + final Message m = PooledLambda.obtainMessage( + WindowProcessListener::clearWaitingToKill, mListener); + mAtm.mH.sendMessage(m); + } + + void addPackage(String pkg, long versionCode) { + // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are + // using WM lock. Need to figure-out if it is okay to do this asynchronously. + if (mListener == null) return; + mListener.addPackage(pkg, versionCode); + } + + ProfilerInfo onStartActivity(int topProcessState) { + // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are + // using WM lock. Need to figure-out if it is okay to do this asynchronously. + if (mListener == null) return null; + return mListener.onStartActivity(topProcessState); + } + + public void appDied() { + // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are + // using WM lock. Need to figure-out if it is okay to do this asynchronously. + if (mListener == null) return; + mListener.appDied(); + } + @Override public void onConfigurationChanged(Configuration newGlobalConfig) { super.onConfigurationChanged(newGlobalConfig); diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java index 9cad6fe47563..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,7 @@ package com.android.server.am; +import android.app.ProfilerInfo; import android.content.pm.ApplicationInfo; import android.util.proto.ProtoOutputStream; @@ -51,5 +52,16 @@ 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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index fc76b46f5dcf..d57214ea894c 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -109,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; @@ -129,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; @@ -144,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; @@ -500,6 +504,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * * @see #mCurFocusedWindow */ + @SoftInputModeFlags int mCurFocusedWindowSoftInputMode; /** @@ -517,6 +522,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags */ + @MissingMethodFlags int mCurInputContextMissingMethods; /** @@ -690,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(); @@ -771,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; @@ -834,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); @@ -846,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); @@ -1499,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 @@ -1729,7 +1737,7 @@ 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 @@ -1808,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()); @@ -1855,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)); @@ -1886,9 +1892,8 @@ 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; @@ -1921,7 +1926,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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); @@ -2046,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)); @@ -2088,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); @@ -2166,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); } } } @@ -2477,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. @@ -2560,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); } @@ -2734,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; @@ -2749,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; @@ -2759,12 +2762,10 @@ 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; @@ -2774,14 +2775,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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); @@ -4579,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/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/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/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 6db7e4f1a800..93870e73ecab 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -88,7 +88,6 @@ import com.android.internal.os.KernelWakelockReader; import com.android.internal.os.KernelWakelockStats; import com.android.internal.os.LooperStats; import com.android.internal.os.PowerProfile; -import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.StoragedUidIoStatsReader; import com.android.internal.util.DumpUtils; import com.android.server.BinderCallsStatsService; @@ -175,6 +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") @@ -195,8 +199,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private static IThermalService sThermalService; private File mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_companion"); - @GuardedBy("this") - ProcessCpuTracker mProcessCpuTracker = null; public StatsCompanionService(Context context) { super(); @@ -770,7 +772,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullBluetoothBytesTransfer( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - BluetoothActivityEnergyInfo info = fetchBluetoothData(); + BluetoothActivityEnergyInfo info = pullBluetoothData(); if (info.getUidTraffic() != null) { for (UidTraffic traffic : info.getUidTraffic()) { StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, @@ -882,12 +884,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { long token = Binder.clearCallingIdentity(); - synchronized (this) { - if (mWifiManager == null) { - mWifiManager = - IWifiManager.Stub.asInterface( - ServiceManager.getService(Context.WIFI_SERVICE)); - } + if (mWifiManager == null) { + mWifiManager = + IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE)); } if (mWifiManager != null) { try { @@ -917,10 +916,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { long token = Binder.clearCallingIdentity(); - synchronized (this) { - if (mTelephony == null) { - mTelephony = TelephonyManager.from(mContext); - } + if (mTelephony == null) { + mTelephony = TelephonyManager.from(mContext); } if (mTelephony != null) { SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); @@ -944,7 +941,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullBluetoothActivityInfo( int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - BluetoothActivityEnergyInfo info = fetchBluetoothData(); + BluetoothActivityEnergyInfo info = pullBluetoothData(); StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); e.writeLong(info.getTimeStamp()); e.writeInt(info.getBluetoothStackState()); @@ -955,7 +952,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pulledData.add(e); } - private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() { + private synchronized BluetoothActivityEnergyInfo pullBluetoothData() { final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver( @@ -1326,35 +1323,30 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - 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); + 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); } } @@ -1423,34 +1415,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { }); } - private void pullProcessCpuTime(int tagId, long elapsedNanos, final long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - synchronized (this) { - if (mProcessCpuTracker == null) { - mProcessCpuTracker = new ProcessCpuTracker(false); - mProcessCpuTracker.init(); - } - mProcessCpuTracker.update(); - for (int i = 0; i < mProcessCpuTracker.countStats(); i++) { - ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, - wallClockNanos); - e.writeInt(st.uid); - e.writeString(st.name); - e.writeLong(st.base_utime); - e.writeLong(st.base_stime); - pulledData.add(e); - } - } - } - /** * Pulls various data. */ @Override // Binder call public StatsLogEventWrapper[] pullData(int tagId) { enforceCallingPermission(); - if (DEBUG) { Slog.d(TAG, "Pulling " + tagId); } @@ -1563,8 +1533,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { break; } case StatsLog.PROC_STATS: { - pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, - ret); + pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret); break; } case StatsLog.PROC_STATS_PKG_PROC: { @@ -1580,10 +1549,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret); break; } - case StatsLog.PROCESS_CPU_TIME: { - pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret); - break; - } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; 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/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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7342ba195796..056e92e3b654 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2675,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(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index b06be1a9a11e..2dbbf55a9347 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -70,7 +70,8 @@ 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; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 70cdba28ac83..b88165ef0f24 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -206,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; @@ -7871,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)) { @@ -9373,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 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/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 cc7a24d5700e..2c993d32c20c 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -25,7 +25,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT 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; @@ -307,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. 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/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 58fe70d7d992..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; } @@ -173,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); @@ -192,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; @@ -487,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}. @@ -495,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); } 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/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/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 921cdea2e646..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 ActivityTaskManagerService mService; - @Before public void setUp() throws Exception { super.setUp(); TaskRecord.setTaskRecordFactory(null); - mService = createActivityTaskManagerService(); + setupActivityTaskManagerService(); } @Test 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/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/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/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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 3127b3584dd9..d33a537f2194 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -676,7 +676,7 @@ public class TelecomManager { /** * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public static TelecomManager from(Context context) { return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); } diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 1efa906fb407..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; } 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/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index bfbcd5751bf4..e0ec2c50ab5b 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -21,7 +21,6 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -247,7 +246,7 @@ public class ServiceState implements Parcelable { private String mDataOperatorAlphaLong; private String mDataOperatorAlphaShort; private String mDataOperatorNumeric; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage private boolean mIsManualNetworkSelection; private boolean mIsEmergencyOnly; @@ -257,9 +256,9 @@ public class ServiceState implements Parcelable { @UnsupportedAppUsage private boolean mCssIndicator; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage private int mNetworkId; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage private int mSystemId; @UnsupportedAppUsage private int mCdmaRoamingIndicator; @@ -457,7 +456,7 @@ public class ServiceState implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public int getVoiceRegState() { return mVoiceRegState; } @@ -472,7 +471,7 @@ public class ServiceState implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public int getDataRegState() { return mDataRegState; } @@ -533,7 +532,7 @@ public class ServiceState implements Parcelable { * @return roaming status * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public boolean getVoiceRoaming() { return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING; } @@ -557,7 +556,7 @@ public class ServiceState implements Parcelable { * @return roaming type * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public boolean getDataRoaming() { return getDataRoamingType() != ROAMING_TYPE_NOT_ROAMING; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index d0c6c49e18d6..3b4016437e9a 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1368,7 +1368,7 @@ public class SubscriptionManager { } /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public static int getPhoneId(int subId) { if (!isValidSubscriptionId(subId)) { if (DBG) { @@ -1664,7 +1664,7 @@ public class SubscriptionManager { * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public static boolean isUsableSubIdValue(int subId) { return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE; } @@ -1682,7 +1682,7 @@ public class SubscriptionManager { } /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) { int[] subIds = SubscriptionManager.getSubId(phoneId); if (subIds != null && subIds.length > 0) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index d6a0ae13f003..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; @@ -230,8 +232,7 @@ public class TelephonyManager { /** @hide /* @deprecated - use getSystemService as described above */ - @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public static TelephonyManager getDefault() { return sInstance; } @@ -320,7 +321,7 @@ public class TelephonyManager { } /** {@hide} */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public static TelephonyManager from(Context context) { return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); } @@ -706,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"; @@ -1231,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 @@ -1278,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. @@ -1307,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 * @@ -1339,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) @@ -1355,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 */ @@ -1408,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) @@ -1423,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 */ @@ -1732,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); @@ -1916,7 +1949,7 @@ public class TelephonyManager { * @param subId * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getNetworkOperatorName(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, ""); @@ -1944,7 +1977,7 @@ public class TelephonyManager { * @param subId * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getNetworkOperator(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getNetworkOperatorForPhone(phoneId); @@ -2268,7 +2301,7 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public int getDataNetworkType(int subId) { try{ ITelephony telephony = getITelephony(); @@ -2304,7 +2337,7 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public int getVoiceNetworkType(int subId) { try{ ITelephony telephony = getITelephony(); @@ -2787,7 +2820,7 @@ public class TelephonyManager { * @param subId for which SimOperator is returned * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSimOperator(int subId) { return getSimOperatorNumeric(subId); } @@ -2801,7 +2834,7 @@ public class TelephonyManager { * @see #getSimState * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSimOperatorNumeric() { int subId = mSubId; if (!SubscriptionManager.isUsableSubIdValue(subId)) { @@ -2830,7 +2863,7 @@ public class TelephonyManager { * @param subId for which SimOperator is returned * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSimOperatorNumeric(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getSimOperatorNumericForPhone(phoneId); @@ -2844,7 +2877,7 @@ public class TelephonyManager { * @param phoneId for which SimOperator is returned * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSimOperatorNumericForPhone(int phoneId) { return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, ""); @@ -2871,7 +2904,7 @@ public class TelephonyManager { * @param subId for which SimOperatorName is returned * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSimOperatorName(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getSimOperatorNameForPhone(phoneId); @@ -2901,7 +2934,7 @@ public class TelephonyManager { * @param subId for which SimCountryIso is returned * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSimCountryIso(int subId) { int phoneId = SubscriptionManager.getPhoneId(subId); return getSimCountryIsoForPhone(phoneId); @@ -2923,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) @@ -2940,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 @@ -3085,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) @@ -3103,17 +3136,17 @@ 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 */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getSubscriberId(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -3498,7 +3531,7 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public String getMsisdn(int subId) { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -4431,7 +4464,7 @@ public class TelephonyManager { /** * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage private ITelephony getITelephony() { return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); } @@ -7404,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; } @@ -8009,7 +8044,7 @@ public class TelephonyManager { * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public ServiceState getServiceStateForSubscriber(int subId) { try { ITelephony service = getITelephony(); @@ -8343,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/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/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 006b04036dca..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; @@ -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. */ @@ -1505,4 +1502,117 @@ interface ITelephony { * */ 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/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/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. */ |