diff options
227 files changed, 9145 insertions, 6910 deletions
diff --git a/Android.bp b/Android.bp index f9b60e67e659..d94bd8439349 100644 --- a/Android.bp +++ b/Android.bp @@ -560,6 +560,7 @@ java_defaults { "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl", "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl", "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl", + "telephony/java/com/android/internal/telephony/IRcs.aidl", "telephony/java/com/android/internal/telephony/ISms.aidl", "telephony/java/com/android/internal/telephony/ISub.aidl", "telephony/java/com/android/internal/telephony/IAns.aidl", @@ -1220,11 +1221,6 @@ stubs_defaults { srcs_lib_whitelist_dirs: frameworks_base_subdirs, srcs_lib_whitelist_pkgs: packages_to_document, libs: [ - "core-oj", - "core-libart", - "conscrypt", - "bouncycastle", - "okhttp", "ext", "framework", "voip-common", diff --git a/Android.mk b/Android.mk index ba153ee76b0a..770ec20f151e 100644 --- a/Android.mk +++ b/Android.mk @@ -113,6 +113,17 @@ $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \ $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)) $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)) +$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \ + frameworks/base/tools/hiddenapi/merge_csv.py \ + $(PRIVATE_METADATA_INPUTS) + frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@ + +$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST)) +$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)) +$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)) +$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)) +$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA)) + # Include subdirectory makefiles # ============================================================ diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index ff40f7543c9d..5c212213f2d1 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -5,6 +5,7 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL packages/PrintRecommendationService/ packages/PrintSpooler/ packages/PackageInstaller/ + packages/SystemUI/ services/print/ services/usb/ telephony/ diff --git a/api/current.txt b/api/current.txt index 71793c0222d7..1983fb483ecd 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 @@ -9167,6 +9168,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 +9196,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 +9205,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 +9322,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); @@ -15468,8 +15475,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 +27453,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 +27725,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 +29730,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 +29808,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 +29821,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 +36818,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 +36940,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 +37002,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 +37025,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 +37053,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 @@ -43041,9 +43114,8 @@ package android.telephony { method public static int getDefaultSmsSubscriptionId(); method public static int getDefaultSubscriptionId(); method public static int getDefaultVoiceSubscriptionId(); - method public static int getSlotIndex(int); - method public static int[] getSubscriptionIds(int); method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int); + method public static int getSlotIndex(int); method public static int[] getSubscriptionIds(int); method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); method public boolean isActiveSubscriptionId(int); @@ -43439,7 +43511,8 @@ package android.telephony.data { package android.telephony.emergency { - public final class EmergencyNumber implements android.os.Parcelable { + public final class EmergencyNumber implements java.lang.Comparable android.os.Parcelable { + method public int compareTo(android.telephony.emergency.EmergencyNumber); method public int describeContents(); method public java.lang.String getCountryIso(); method public int getEmergencyNumberSourceBitmask(); @@ -45199,6 +45272,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 +51424,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 +51465,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(); @@ -52310,6 +52418,7 @@ package android.webkit { field public static final int ERROR_UNSAFE_RESOURCE = -16; // 0xfffffff0 field public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3; // 0xfffffffd field public static final int ERROR_UNSUPPORTED_SCHEME = -10; // 0xfffffff6 + field public static final int SAFE_BROWSING_THREAT_BILLING = 4; // 0x4 field public static final int SAFE_BROWSING_THREAT_MALWARE = 1; // 0x1 field public static final int SAFE_BROWSING_THREAT_PHISHING = 2; // 0x2 field public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0; // 0x0 diff --git a/api/system-current.txt b/api/system-current.txt index 87a44ced026e..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); @@ -5604,17 +5622,12 @@ package android.telephony.data { } public final class DataProfile implements android.os.Parcelable { - ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean); - ctor public DataProfile(android.os.Parcel); - method public int describeContents(); method public java.lang.String getApn(); method public int getAuthType(); method public int getBearerBitmap(); method public int getMaxConns(); method public int getMaxConnsTime(); method public int getMtu(); - method public java.lang.String getMvnoMatchData(); - method public java.lang.String getMvnoType(); method public java.lang.String getPassword(); method public int getProfileId(); method public java.lang.String getProtocol(); @@ -5624,9 +5637,8 @@ package android.telephony.data { method public java.lang.String getUserName(); method public int getWaitTime(); method public boolean isEnabled(); - method public boolean isModemCognitive(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR; + method public boolean isPersistent(); + method public boolean isPreferred(); field public static final int TYPE_3GPP = 1; // 0x1 field public static final int TYPE_3GPP2 = 2; // 0x2 field public static final int TYPE_COMMON = 0; // 0x0 @@ -5952,11 +5964,13 @@ package android.telephony.ims { } public final class ImsExternalCallState implements android.os.Parcelable { + ctor public ImsExternalCallState(java.lang.String, android.net.Uri, android.net.Uri, boolean, int, int, boolean); method public int describeContents(); method public android.net.Uri getAddress(); method public int getCallId(); method public int getCallState(); method public int getCallType(); + method public android.net.Uri getLocalAddress(); method public boolean isCallHeld(); method public boolean isCallPullable(); method public void writeToParcel(android.os.Parcel, int); @@ -6376,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/Android.mk b/cmds/statsd/Android.mk index c396cd130f93..5818f5d550c3 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -46,6 +46,7 @@ statsd_common_src := \ src/logd/LogEvent.cpp \ src/logd/LogListener.cpp \ src/matchers/CombinationLogMatchingTracker.cpp \ + src/matchers/EventMatcherWizard.cpp \ src/matchers/matcher_util.cpp \ src/matchers/SimpleLogMatchingTracker.cpp \ src/metrics/MetricProducer.cpp \ @@ -217,6 +218,7 @@ LOCAL_SRC_FILES := \ tests/metrics/metrics_test_helper.cpp \ tests/statsd_test_util.cpp \ tests/e2e/WakelockDuration_e2e_test.cpp \ + tests/e2e/MetricActivation_e2e_test.cpp \ tests/e2e/MetricConditionLink_e2e_test.cpp \ tests/e2e/Alarm_e2e_test.cpp \ tests/e2e/Attribution_e2e_test.cpp \ diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 6a9e8a1bf688..3e8b9b8d5df5 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -194,7 +194,7 @@ private: FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents); + FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); @@ -219,6 +219,7 @@ private: FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); + FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 10b7c0b0117d..39d24ee2fe6c 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. @@ -2356,10 +2357,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; @@ -3049,3 +3050,17 @@ message ProcessCpuTime { // Process cpu time in system space, cumulative from boot/process start optional int64 system_time_millis = 4; } + +/** + * Logs when a user restriction was added or removed. + * + * Logged from: + * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java + */ +message UserRestrictionChanged { + // The raw string of the user restriction as defined in UserManager. + // Allowed values are defined in UserRestrictionsUtils#USER_RESTRICTIONS. + optional string restriction = 1; + // Whether the restriction is enabled or disabled. + optional bool enabled = 2; +}
\ No newline at end of file diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.cpp b/cmds/statsd/src/matchers/EventMatcherWizard.cpp new file mode 100644 index 000000000000..8418e9833509 --- /dev/null +++ b/cmds/statsd/src/matchers/EventMatcherWizard.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "EventMatcherWizard.h" +#include <unordered_set> + +namespace android { +namespace os { +namespace statsd { + +using std::map; +using std::string; +using std::vector; + +MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) { + if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) { + return MatchingState::kNotComputed; + } + vector<MatchingState> matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed); + mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache); + return matcherCache[matcher_index]; +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h new file mode 100644 index 000000000000..57ec2b35ba32 --- /dev/null +++ b/cmds/statsd/src/matchers/EventMatcherWizard.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "LogMatchingTracker.h" + +namespace android { +namespace os { +namespace statsd { + +class EventMatcherWizard : public virtual android::RefBase { +public: + EventMatcherWizard(){}; // for testing + EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers) + : mAllEventMatchers(eventTrackers){}; + + virtual ~EventMatcherWizard(){}; + + MatchingState matchLogEvent(const LogEvent& event, int matcher_index); + +private: + std::vector<sp<LogMatchingTracker>> mAllEventMatchers; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 02b97734d3ff..f5a16e96fdf5 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -69,11 +69,16 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8; GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int pullTagId, + const sp<ConditionWizard>& wizard, + const int whatMatcherIndex, + const sp<EventMatcherWizard>& matcherWizard, + const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard), + mWhatMatcherIndex(whatMatcherIndex), + mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), mPullTagId(pullTagId), mTriggerAtomId(triggerAtomId), @@ -136,7 +141,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; if (mIsPulled) { - pullLocked(startTimeNs); + pullAndMatchEventsLocked(startTimeNs); } VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d", @@ -302,7 +307,7 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, mPastBuckets.clear(); } -void GaugeMetricProducer::pullLocked(const int64_t timestampNs) { +void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { bool triggerPuller = false; switch(mSamplingType) { // When the metric wants to do random sampling and there is already one gauge atom for the @@ -331,7 +336,10 @@ void GaugeMetricProducer::pullLocked(const int64_t timestampNs) { return; } for (const auto& data : allData) { - onMatchedLogEventLocked(0, *data); + if (mEventMatcherWizard->matchLogEvent( + *data, mWhatMatcherIndex) == MatchingState::kMatched) { + onMatchedLogEventLocked(mWhatMatcherIndex, *data); + } } } @@ -341,7 +349,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, flushIfNeededLocked(eventTimeNs); mCondition = conditionMet; if (mIsPulled) { - pullLocked(eventTimeNs); + pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. } @@ -354,7 +362,7 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition // pull for every dimension. mCondition = overallCondition; if (mIsPulled) { - pullLocked(eventTimeNs); + pullAndMatchEventsLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. } @@ -387,7 +395,10 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven return; } for (const auto& data : allData) { - onMatchedLogEventLocked(0, *data); + if (mEventMatcherWizard->matchLogEvent( + *data, mWhatMatcherIndex) == MatchingState::kMatched) { + onMatchedLogEventLocked(mWhatMatcherIndex, *data); + } } } @@ -426,7 +437,7 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( flushIfNeededLocked(eventTimeNs); if (mTriggerAtomId == event.GetTagId()) { - pullLocked(eventTimeNs); + pullAndMatchEventsLocked(eventTimeNs); return; } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 6379389218fd..99827bb2ad2d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -24,6 +24,7 @@ #include "../external/PullDataReceiver.h" #include "../external/StatsPullerManager.h" #include "../matchers/matcher_util.h" +#include "../matchers/EventMatcherWizard.h" #include "MetricProducer.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "../stats_util.h" @@ -56,7 +57,9 @@ typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { public: GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric, - const int conditionIndex, const sp<ConditionWizard>& wizard, + const int conditionIndex, const sp<ConditionWizard>& conditionWizard, + const int whatMatcherIndex, + const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager); @@ -78,7 +81,7 @@ public: flushCurrentBucketLocked(eventTimeNs); mCurrentBucketStartTimeNs = eventTimeNs; if (mIsPulled) { - pullLocked(eventTimeNs); + pullAndMatchEventsLocked(eventTimeNs); } }; @@ -113,7 +116,11 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs) override; - void pullLocked(const int64_t timestampNs); + void pullAndMatchEventsLocked(const int64_t timestampNs); + + const int mWhatMatcherIndex; + + sp<EventMatcherWizard> mEventMatcherWizard; sp<StatsPullerManager> mPullerManager; // tagId for pulled data. -1 if this is not pulled diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index df081817232e..f87849edae7a 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -64,8 +64,54 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo onMatchedLogEventInternalLocked( matcherIndex, metricKey, conditionKey, condition, event); } +} + +bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { + bool isActive = mEventActivationMap.empty(); + for (auto& it : mEventActivationMap) { + if (it.second.state == ActivationState::kActive && + elapsedTimestampNs > it.second.ttl_ns + it.second.activation_ns) { + it.second.state = ActivationState::kNotActive; + } + if (it.second.state == ActivationState::kActive) { + isActive = true; + } + } + return isActive; +} + +void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { + std::lock_guard<std::mutex> lock(mMutex); + if (!mIsActive) { + return; + } + mIsActive = evaluateActiveStateLocked(elapsedTimestampNs); + if (!mIsActive) { + flushLocked(elapsedTimestampNs); + } +} + +void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) { + std::lock_guard<std::mutex> lock(mMutex); + // When a metric producer does not depend on any activation, its mIsActive is true. + // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not + // change. + if (mEventActivationMap.empty()) { + mIsActive = false; + } + mEventActivationMap[activationTrackerIndex].ttl_ns = ttl_seconds * NS_PER_SEC; +} + +void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) { + auto it = mEventActivationMap.find(activationTrackerIndex); + if (it == mEventActivationMap.end()) { + return; + } + it->second.activation_ns = elapsedTimestampNs; + it->second.state = ActivationState::kActive; + mIsActive = true; +} - } } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 6fe4bfb47a1f..b21fd501c2d0 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -34,6 +34,17 @@ namespace android { namespace os { namespace statsd { +// If the metric has no activation requirement, it will be active once the metric producer is +// created. +// If the metric needs to be activated by atoms, the metric producer will start +// with kNotActive state, turn to kActive when the activation event arrives, become kNotActive +// when it reaches the duration limit (timebomb). If the activation event arrives again before +// or after it expires, the event producer will be re-activated and ttl will be reset. +enum ActivationState { + kNotActive = 0, + kActive = 1, +}; + // A MetricProducer is responsible for compute one single metrics, creating stats log report, and // writing the report to dropbox. MetricProducers should respond to package changes as required in // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can @@ -54,7 +65,8 @@ public: mContainANYPositionInDimensionsInWhat(false), mSliceByPositionALL(false), mSameConditionDimensionsInTracker(false), - mHasLinksToAllConditionDimensionsInTracker(false) { + mHasLinksToAllConditionDimensionsInTracker(false), + mIsActive(true) { } virtual ~MetricProducer(){}; @@ -93,17 +105,23 @@ public: // Consume the parsed stats log entry that already matched the "what" of the metric. void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { std::lock_guard<std::mutex> lock(mMutex); - onMatchedLogEventLocked(matcherIndex, event); + if (mIsActive) { + onMatchedLogEventLocked(matcherIndex, event); + } } void onConditionChanged(const bool condition, const int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); - onConditionChangedLocked(condition, eventTime); + if (mIsActive) { + onConditionChangedLocked(condition, eventTime); + } } void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); - onSlicedConditionMayChangeLocked(overallCondition, eventTime); + if (mIsActive) { + onSlicedConditionMayChangeLocked(overallCondition, eventTime); + } } bool isConditionSliced() const { @@ -177,6 +195,15 @@ public: return mCurrentBucketNum; } + void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) { + std::lock_guard<std::mutex> lock(mMutex); + activateLocked(activationTrackerIndex, elapsedTimestampNs); + } + + void addActivation(int activationTrackerIndex, int64_t ttl_seconds); + + void flushIfExpire(int64_t elapsedTimestampNs); + protected: virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, @@ -189,6 +216,10 @@ protected: virtual size_t byteSizeLocked() const = 0; virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; + bool evaluateActiveStateLocked(int64_t elapsedTimestampNs); + + void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); + /** * Flushes the current bucket if the eventTime is after the current bucket's end time. This will also flush the current partial bucket in memory. @@ -198,9 +229,9 @@ protected: /** * Flushes all the data including the current partial bucket. */ - virtual void flushLocked(const int64_t& eventTime) { - flushIfNeededLocked(eventTime); - flushCurrentBucketLocked(eventTime); + virtual void flushLocked(const int64_t& eventTimeNs) { + flushIfNeededLocked(eventTimeNs); + flushCurrentBucketLocked(eventTimeNs); }; /** @@ -295,6 +326,21 @@ protected: virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); mutable std::mutex mMutex; + + struct Activation { + Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive) {} + + int64_t ttl_ns; + int64_t activation_ns; + ActivationState state; + }; + // When the metric producer has multiple activations, these activations are ORed to determine + // whether the metric producer is ready to generate metrics. + std::unordered_map<int, Activation> mEventActivationMap; + + bool mIsActive; + + FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 0e5ef4d3e59a..f85ba1f93e8c 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -73,7 +73,8 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, - mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); + mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, + mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); @@ -298,7 +299,12 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } int tagId = event.GetTagId(); - int64_t eventTime = event.GetElapsedTimestampNs(); + int64_t eventTimeNs = event.GetElapsedTimestampNs(); + + for (int metric : mMetricIndexesWithActivation) { + mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); + } + if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; @@ -310,6 +316,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); } + for (const auto& it : mActivationAtomTrackerToMetricMap) { + if (matcherCache[it.first] == MatchingState::kMatched) { + for (int metricIndex : it.second) { + mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); + } + } + } + // A bitmap to see which ConditionTracker needs to be re-evaluated. vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); @@ -347,13 +361,13 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // Push the new condition to it directly. if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], - eventTime); + eventTimeNs); // metric cares about sliced conditions, and it may have changed. Send // notification, and the metric can query the sliced conditions that are // interesting to it. } else { mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], - eventTime); + eventTimeNs); } } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index dfbb69f1ab7c..649222ffef9d 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -195,6 +195,11 @@ private: // maps from ConditionTracker to MetricProducer std::unordered_map<int, std::vector<int>> mConditionToMetricMap; + // maps from life span triggering event to MetricProducers. + std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap; + + std::vector<int> mMetricIndexesWithActivation; + void initLogSourceWhiteList(); // The metrics that don't need to be uploaded or even reported. @@ -207,7 +212,7 @@ private: FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents); FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm); - FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents); + FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents); FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition); @@ -230,6 +235,7 @@ private: FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); + FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 75d6df95852d..136ba074d589 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -25,6 +25,7 @@ #include "../external/StatsPullerManager.h" #include "../matchers/CombinationLogMatchingTracker.h" #include "../matchers/SimpleLogMatchingTracker.h" +#include "../matchers/EventMatcherWizard.h" #include "../metrics/CountMetricProducer.h" #include "../metrics/DurationMetricProducer.h" #include "../metrics/EventMetricProducer.h" @@ -294,6 +295,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t unordered_map<int, std::vector<int>>& trackerToMetricMap, unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) { sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers); + sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers); const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() + config.event_metric_size() + config.value_metric_size(); allMetricProducers.reserve(allMetricsCount); @@ -563,7 +565,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } sp<MetricProducer> gaugeProducer = new GaugeMetricProducer( - key, metric, conditionIndex, wizard, pullTagId, triggerAtomId, atomTagId, + key, metric, conditionIndex, wizard, + trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs, pullerManager); allMetricProducers.push_back(gaugeProducer); } @@ -682,6 +685,44 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key, return true; } +bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, + const int64_t currentTimeNs, + const unordered_map<int64_t, int> &logEventTrackerMap, + const unordered_map<int64_t, int> &metricProducerMap, + vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + for (int i = 0; i < config.metric_activation_size(); ++i) { + const MetricActivation& metric_activation = config.metric_activation(i); + auto itr = metricProducerMap.find(metric_activation.metric_id()); + if (itr == metricProducerMap.end()) { + ALOGE("Metric id not found in metric activation: %lld", + (long long)metric_activation.metric_id()); + return false; + } + const int metricTrackerIndex = itr->second; + if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) { + ALOGE("Invalid metric tracker index."); + return false; + } + metricsWithActivation.push_back(metricTrackerIndex); + for (int j = 0; j < metric_activation.event_activation_size(); ++j) { + const EventActivation& activation = metric_activation.event_activation(j); + auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id()); + if (logTrackerIt == logEventTrackerMap.end()) { + ALOGE("Atom matcher not found for event activation."); + return false; + } + const int atomMatcherIndex = logTrackerIt->second; + activationAtomTrackerToMetricMap[atomMatcherIndex].push_back( + metricTrackerIndex); + allMetricProducers[metricTrackerIndex]->addActivation( + atomMatcherIndex, activation.ttl_seconds()); + } + } + return true; +} + bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, @@ -695,6 +736,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& unordered_map<int, std::vector<int>>& conditionToMetricMap, unordered_map<int, std::vector<int>>& trackerToMetricMap, unordered_map<int, std::vector<int>>& trackerToConditionMap, + unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) { unordered_map<int64_t, int> logTrackerMap; unordered_map<int64_t, int> conditionTrackerMap; @@ -729,6 +772,11 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initAlarms failed"); return false; } + if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap, + allMetricProducers, activationAtomTrackerToMetricMap, metricsWithActivation)) { + ALOGE("initMetricActivations failed"); + return false; + } return true; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index c6601493135f..9ffcedae4962 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -108,6 +108,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& std::unordered_map<int, std::vector<int>>& conditionToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToMetricMap, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, + unordered_map<int, std::vector<int>>& lifeSpanEventTrackerToMetricMap, + vector<int>& metricsWithLifeSpan, std::set<int64_t>& noReportMetricIds); bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys); diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index d19e247ae6c7..d5f81a593082 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -347,6 +347,17 @@ message Subscription { optional float probability_of_informing = 7 [default = 1.1]; } +message EventActivation { + optional int64 atom_matcher_id = 1; + optional int64 ttl_seconds = 2; +} + +message MetricActivation { + optional int64 metric_id = 1; + + repeated EventActivation event_activation = 2; +} + message StatsdConfig { optional int64 id = 1; @@ -384,6 +395,8 @@ message StatsdConfig { optional bool hash_strings_in_metric_report = 16 [default = true]; + repeated MetricActivation metric_activation = 17; + // Field number 1000 is reserved for later use. reserved 1000; } diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 8fbb58a956d5..f8184d8aa14c 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -282,13 +282,17 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); EXPECT_EQ(1u, allMetricProducers.size()); EXPECT_EQ(1u, allAnomalyTrackers.size()); EXPECT_EQ(1u, noReportMetricIds.size()); @@ -309,13 +313,17 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { @@ -333,13 +341,17 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { @@ -357,12 +369,16 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { @@ -380,12 +396,16 @@ TEST(MetricsManagerTest, TestMissingPredicate) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { @@ -403,13 +423,17 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { @@ -427,13 +451,17 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; + unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap; + vector<int> metricsWithLifeSpan; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap, noReportMetricIds)); + trackerToMetricMap, trackerToConditionMap, + lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan, + noReportMetricIds)); } #else diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index 5729febce4e2..d7b9c119b71b 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -200,8 +200,8 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_deci_celsius(), 0); } -TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) { - auto config = CreateStatsdConfig(GaugeMetric::ALL_CONDITION_CHANGES); +TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) { + auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE); int64_t baseTimeNs = 10 * NS_PER_SEC; int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs; int64_t bucketSizeNs = diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp new file mode 100644 index 000000000000..0f13a4ac1254 --- /dev/null +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -0,0 +1,242 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <gtest/gtest.h> + +#include "src/StatsLogProcessor.h" +#include "src/stats_log_util.h" +#include "tests/statsd_test_util.h" + +#include <vector> + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +namespace { + +StatsdConfig CreateStatsdConfig() { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto crashMatcher = CreateProcessCrashAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher(); + + *config.add_atom_matcher() = saverModeMatcher; + *config.add_atom_matcher() = crashMatcher; + *config.add_atom_matcher() = screenOnMatcher; + + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(crashMatcher.id()); + countMetric->set_bucket(FIVE_MINUTES); + countMetric->mutable_dimensions_in_what()->set_field( + android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field + + auto metric_activation1 = config.add_metric_activation(); + metric_activation1->set_metric_id(metricId); + auto event_activation1 = metric_activation1->add_event_activation(); + event_activation1->set_atom_matcher_id(saverModeMatcher.id()); + event_activation1->set_ttl_seconds(60 * 6); // 6 minutes + auto event_activation2 = metric_activation1->add_event_activation(); + event_activation2->set_atom_matcher_id(screenOnMatcher.id()); + event_activation2->set_ttl_seconds(60 * 2); // 2 minutes + + return config; +} + +} // namespace + +TEST(MetricActivationE2eTest, TestCountMetric) { + auto config = CreateStatsdConfig(); + + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + sp<MetricProducer> metricProducer = + processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]; + auto& eventActivationMap = metricProducer->mEventActivationMap; + + EXPECT_FALSE(metricProducer->mIsActive); + // Two activations: one is triggered by battery saver mode (tracker index 0), the other is + // triggered by screen on event (tracker index 2). + EXPECT_EQ(eventActivationMap.size(), 2u); + EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); + EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); + EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0].activation_ns, 0); + EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2].activation_ns, 0); + EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + + std::unique_ptr<LogEvent> event; + + event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricProducer->mIsActive); + + // Activated by battery save mode. + event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2].activation_ns, 0); + EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + + // First processed event. + event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); + processor->OnLogEvent(event.get()); + + // Activated by screen on event. + event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + 20); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + + // 2nd processed event. + // The activation by screen_on event expires, but the one by battery save mode is still active. + event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + + // 3rd processed event. + event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); + processor->OnLogEvent(event.get()); + + // All activations expired. + event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); + processor->OnLogEvent(event.get()); + EXPECT_FALSE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + + // Re-activate. + event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, + bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + processor->OnLogEvent(event.get()); + EXPECT_TRUE(metricProducer->mIsActive); + EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive); + EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC); + EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive); + EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC); + + event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); + processor->OnLogEvent(event.get()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, ADB_DUMP, + &buffer); + EXPECT_TRUE(buffer.size() > 0); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStartEndTimestamp(&reports); + EXPECT_EQ(1, reports.reports_size()); + EXPECT_EQ(1, reports.reports(0).metrics_size()); + EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size()); + + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue( + reports.reports(0).metrics(0).count_metrics(), &countMetrics); + EXPECT_EQ(4, countMetrics.data_size()); + + auto data = countMetrics.data(0); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(1); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(2); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + // Partial bucket as metric is deactivated. + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8, + data.bucket_info(0).end_bucket_elapsed_nanos()); + + data = countMetrics.data(3); + EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field()); + EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size()); + EXPECT_EQ(1 /* uid field */, + data.dimensions_in_what().value_tuple().dimensions_value(0).field()); + EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, + data.bucket_info(0).start_bucket_elapsed_nanos()); + EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, + data.bucket_info(0).end_bucket_elapsed_nanos()); + +} + + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index bf58b9c7e4d4..60bd4a7e07d9 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "src/matchers/SimpleLogMatchingTracker.h" #include "src/metrics/GaugeMetricProducer.h" #include "src/stats_log_util.h" #include "logd/LogEvent.h" @@ -40,6 +41,8 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); const int tagId = 1; const int64_t metricId = 123; +const int64_t atomMatcherId = 678; +const int logEventMatcherIndex = 0; const int64_t bucketStartTimeNs = 10 * NS_PER_SEC; const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; @@ -61,11 +64,19 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { gaugeFieldMatcher->add_child()->set_field(3); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); // statsd started long ago. // The metric starts in the middle of the bucket GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager); @@ -86,6 +97,12 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); @@ -103,6 +120,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -178,7 +196,15 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { alert.set_num_buckets(100); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -246,6 +272,12 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); @@ -263,6 +295,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { })); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -315,6 +348,12 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); @@ -330,7 +369,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { return true; })); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, -1, tagId, + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); @@ -388,6 +428,12 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { dim->set_field(conditionTag); dim->add_child()->set_field(1); + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EXPECT_CALL(*wizard, query(_, _, _, _, _, _)) .WillRepeatedly( @@ -420,7 +466,8 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { return true; })); - GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, -1, tagId, + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); @@ -463,7 +510,15 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); gaugeFieldMatcher->set_field(tagId); gaugeFieldMatcher->add_child()->set_field(2); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -542,6 +597,12 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, Pull(tagId, _, _)) .WillOnce(Invoke([](int tagId, int64_t timeNs, @@ -574,6 +635,7 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { int triggerId = 5; GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); @@ -632,6 +694,12 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ + new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, Pull(tagId, _, _)) .WillOnce(Invoke([](int tagId, int64_t timeNs, @@ -667,6 +735,7 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { int triggerId = 5; GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); diff --git a/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/Activity.java b/core/java/android/app/Activity.java index aca80b494b78..3cc5e370bc83 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4678,7 +4678,7 @@ public class Activity extends ContextThemeWrapper if (decor != null) { decor.cancelPendingInputEvents(); } - if (options != null && !isTopOfTask()) { + if (options != null) { mActivityTransitionState.startExitOutTransition(this, options); } } @@ -4882,6 +4882,7 @@ public class Activity extends ContextThemeWrapper Bundle options) throws IntentSender.SendIntentException { try { + options = transferSpringboardActivityOptions(options); String resolvedType = null; if (fillInIntent != null) { fillInIntent.migrateExtraStreamToClipData(); @@ -4898,6 +4899,12 @@ public class Activity extends ContextThemeWrapper throw new IntentSender.SendIntentException(); } Instrumentation.checkStartActivityResult(result, null); + + if (options != null) { + // Only when the options are not null, as the intent can point to something other + // than an Activity. + cancelInputsAndStartExitTransition(options); + } } catch (RemoteException e) { } if (requestCode >= 0) { @@ -6471,7 +6478,7 @@ public class Activity extends ContextThemeWrapper * * @return true if this is the topmost, non-finishing activity in its task. */ - private boolean isTopOfTask() { + final boolean isTopOfTask() { if (mToken == null || mWindow == null) { return false; } diff --git a/core/java/android/app/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/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 3c9a2d4ab7e3..94b42ff960de 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -195,6 +195,13 @@ public class ActivityOptions { private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId"; /** + * See {@link #setPendingIntentLaunchFlags(int)} + * @hide + */ + private static final String KEY_PENDING_INTENT_LAUNCH_FLAGS = + "android.activity.pendingIntentLaunchFlags"; + + /** * See {@link #setTaskOverlay}. * @hide */ @@ -309,6 +316,7 @@ public class ActivityOptions { @WindowConfiguration.ActivityType private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED; private int mLaunchTaskId = -1; + private int mPendingIntentLaunchFlags; private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; private boolean mLockTaskMode = false; private boolean mDisallowEnterPictureInPictureWhileLaunching; @@ -932,6 +940,7 @@ public class ActivityOptions { mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED); mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED); mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1); + mPendingIntentLaunchFlags = opts.getInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, 0); mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false); mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false); mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false); @@ -1233,6 +1242,22 @@ public class ActivityOptions { } /** + * Specifies intent flags to be applied for any activity started from a PendingIntent. + * + * @hide + */ + public void setPendingIntentLaunchFlags(@android.content.Intent.Flags int flags) { + mPendingIntentLaunchFlags = flags; + } + + /** + * @hide + */ + public int getPendingIntentLaunchFlags() { + return mPendingIntentLaunchFlags; + } + + /** * Set's whether the activity launched with this option should be a task overlay. That is the * activity will always be the top activity of the task. If {@param canResume} is true, then * the task will also not be moved to the front of the stack. @@ -1463,6 +1488,9 @@ public class ActivityOptions { if (mLaunchTaskId != -1) { b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId); } + if (mPendingIntentLaunchFlags != 0) { + b.putInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, mPendingIntentLaunchFlags); + } if (mTaskOverlay) { b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay); } diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 9b2bfc5702cb..4b87a647a80b 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -193,6 +193,13 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { */ public static final int MSG_SHARED_ELEMENT_DESTINATION = 107; + /** + * Sent by Activity#startActivity to notify the entering activity that enter animation for + * back is allowed. If this message is not received, the default exit animation will run when + * backing out of an activity (instead of the 'reverse' shared element transition). + */ + public static final int MSG_ALLOW_RETURN_TRANSITION = 108; + private Window mWindow; final protected ArrayList<String> mAllSharedElementNames; final protected ArrayList<View> mSharedElements = new ArrayList<View>(); @@ -346,8 +353,6 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return new ArrayList<View>(mSharedElements); } - public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; } - protected Transition setTargets(Transition transition, boolean add) { if (transition == null || (add && (mTransitioningViews == null || mTransitioningViews.isEmpty()))) { diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index b8f5a8e94283..3201febec8da 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -35,7 +35,7 @@ import java.util.ArrayList; */ class ActivityTransitionState { - private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements"; + private static final String PENDING_EXIT_SHARED_ELEMENTS = "android:pendingExitSharedElements"; private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom"; @@ -43,9 +43,9 @@ class ActivityTransitionState { /** * The shared elements that the calling Activity has said that they transferred to this - * Activity. + * Activity and will be transferred back during exit animation. */ - private ArrayList<String> mEnteringNames; + private ArrayList<String> mPendingExitNames; /** * The names of shared elements that were shared to the called Activity. @@ -112,8 +112,7 @@ class ActivityTransitionState { public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) { if (mExitTransitionCoordinators == null) { - mExitTransitionCoordinators = - new SparseArray<WeakReference<ExitTransitionCoordinator>>(); + mExitTransitionCoordinators = new SparseArray<>(); } WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator); // clean up old references: @@ -132,7 +131,7 @@ class ActivityTransitionState { public void readState(Bundle bundle) { if (bundle != null) { if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) { - mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS); + mPendingExitNames = bundle.getStringArrayList(PENDING_EXIT_SHARED_ELEMENTS); } if (mEnterTransitionCoordinator == null) { mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM); @@ -141,9 +140,21 @@ class ActivityTransitionState { } } + /** + * Returns the element names to be used for exit animation. It caches the list internally so + * that it is preserved through activty destroy and restore. + */ + private ArrayList<String> getPendingExitNames() { + if (mPendingExitNames == null && mEnterTransitionCoordinator != null) { + mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames(); + } + return mPendingExitNames; + } + public void saveState(Bundle bundle) { - if (mEnteringNames != null) { - bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames); + ArrayList<String> pendingExitNames = getPendingExitNames(); + if (pendingExitNames != null) { + bundle.putStringArrayList(PENDING_EXIT_SHARED_ELEMENTS, pendingExitNames); } if (mExitingFrom != null) { bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom); @@ -226,7 +237,7 @@ class ActivityTransitionState { } } else { mEnterTransitionCoordinator.namedViewsReady(null, null); - mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames(); + mPendingExitNames = null; } mExitingFrom = null; @@ -268,7 +279,7 @@ class ActivityTransitionState { } public void clear() { - mEnteringNames = null; + mPendingExitNames = null; mExitingFrom = null; mExitingTo = null; mExitingToView = null; @@ -296,7 +307,8 @@ class ActivityTransitionState { } public boolean startExitBackTransition(final Activity activity) { - if (mEnteringNames == null || mCalledExitCoordinator != null) { + ArrayList<String> pendingExitNames = getPendingExitNames(); + if (pendingExitNames == null || mCalledExitCoordinator != null) { return false; } else { if (!mHasExited) { @@ -315,7 +327,7 @@ class ActivityTransitionState { } mReturnExitCoordinator = new ExitTransitionCoordinator(activity, - activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames, + activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames, null, null, true); if (enterViewsTransition != null && decor != null) { enterViewsTransition.resume(decor); diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index ab847fd562a4..bce243cc6108 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -65,6 +65,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private OneShotPreDrawListener mViewsReadyListener; private final boolean mIsCrossTask; private Drawable mReplacedBackground; + private ArrayList<String> mPendingExitNames; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) { @@ -249,6 +250,11 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { case MSG_CANCEL: cancel(); break; + case MSG_ALLOW_RETURN_TRANSITION: + if (!mIsCanceled) { + mPendingExitNames = mAllSharedElementNames; + } + break; } } @@ -256,6 +262,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { return mIsReturning && mResultReceiver != null; } + public ArrayList<String> getPendingExitSharedElementNames() { + return mPendingExitNames; + } + /** * This is called onResume. If an Activity is resuming and the transitions * haven't started yet, force the views to appear. This is likely to be diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index df31da9183f1..48a711e79c39 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -433,6 +433,11 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { if (!mSharedElementNotified) { mSharedElementNotified = true; delayCancel(); + + if (!mActivity.isTopOfTask()) { + mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null); + } + if (mListener == null) { mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle); notifyExitComplete(); diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java index ceb828b64add..0e428ae164a4 100644 --- a/core/java/android/app/FragmentTransition.java +++ b/core/java/android/app/FragmentTransition.java @@ -1013,6 +1013,11 @@ class FragmentTransition { replaceTargets(sharedElementTransition, sharedElementsIn, null); } } + + @Override + public void onTransitionEnd(Transition transition) { + transition.removeListener(this); + } }); } diff --git a/core/java/android/app/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/FileUtils.java b/core/java/android/os/FileUtils.java index a9cb0d9579c9..f71fdd7fdac1 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1198,6 +1198,19 @@ public class FileUtils { /** {@hide} */ public static int translateModeStringToPosix(String mode) { + // Sanity check for invalid chars + for (int i = 0; i < mode.length(); i++) { + switch (mode.charAt(i)) { + case 'r': + case 'w': + case 't': + case 'a': + break; + default: + throw new IllegalArgumentException("Bad mode: " + mode); + } + } + int res = 0; if (mode.startsWith("rw")) { res |= O_RDWR | O_CREAT; diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 384115b2d8f1..0c56d4884105 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -19,6 +19,7 @@ package android.os; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.opengl.EGL14; import android.os.Build; @@ -30,6 +31,7 @@ import dalvik.system.VMRuntime; import java.io.BufferedReader; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -54,6 +56,7 @@ public class GraphicsEnvironment { private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0"; private static final String ANGLE_PACKAGE_NAME = "com.android.angle"; private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE"; + private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private ClassLoader mClassLoader; private String mLayerPath; @@ -250,8 +253,40 @@ public class GraphicsEnvironment { if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths); + // Pass the rules file to loader for ANGLE decisions + AssetManager angleAssets = null; + try { + angleAssets = + context.getPackageManager().getResourcesForApplication(angleInfo).getAssets(); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'"); + return; + } + + AssetFileDescriptor assetsFd = null; + try { + assetsFd = angleAssets.openFd(ANGLE_RULES_FILE); + } catch (IOException e) { + Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from " + + "'" + ANGLE_PACKAGE_NAME + "'"); + return; + } + + FileDescriptor rulesFd = null; + long rulesOffset = 0; + long rulesLength = 0; + if (assetsFd != null) { + rulesFd = assetsFd.getFileDescriptor(); + rulesOffset = assetsFd.getStartOffset(); + rulesLength = assetsFd.getLength(); + } else { + Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE); + return; + } + // Further opt-in logic is handled in native, so pass relevant info down - setAngleInfo(paths, packageName, appPref, devOptIn); + setAngleInfo(paths, packageName, appPref, devOptIn, + rulesFd, rulesOffset, rulesLength); } /** @@ -391,5 +426,6 @@ public class GraphicsEnvironment { private static native void setDebugLayers(String layers); private static native void setDriverPath(String path); private static native void setAngleInfo(String path, String appPackage, String appPref, - boolean devOptIn); + boolean devOptIn, FileDescriptor rulesFd, + long rulesOffset, long rulesLength); } diff --git a/core/java/android/os/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/provider/Settings.java b/core/java/android/provider/Settings.java index 22bce1d97a7c..ad640219a6a5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12381,6 +12381,28 @@ public final class Settings { "sms_access_restriction_enabled"; /** + * If set to 1, an app must have the READ_PRIVILEGED_PHONE_STATE permission (or be a device + * / profile owner with the READ_PHONE_STATE permission) to access device identifiers. + * + * STOPSHIP: Remove this once we ship with the new device identifier check enabled. + * + * @hide + */ + public static final String PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED = + "privileged_device_identifier_check_enabled"; + + /** + * If set to 1, an app that is targeting Q and does not meet the new requirements to access + * device identifiers will receive a SecurityException. + * + * STOPSHIP: Remove this once we ship with the new device identifier check enabled. + * + * @hide + */ + public static final String PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED = + "privileged_device_identifier_target_q_behavior_enabled"; + + /** * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored * and restoring to lower version of platform API will be skipped. * 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..f846a356d8fa 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -23,6 +23,7 @@ import android.content.res.TypedArray; import android.graphics.LeakyTypefaceStorage; import android.graphics.Typeface; import android.graphics.fonts.Font; +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. */ @@ -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 bdd7a0900213..300bb6fd4890 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -282,19 +282,28 @@ public class WebViewClient { SAFE_BROWSING_THREAT_UNKNOWN, SAFE_BROWSING_THREAT_MALWARE, SAFE_BROWSING_THREAT_PHISHING, - SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE + SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE, + SAFE_BROWSING_THREAT_BILLING, }) @Retention(RetentionPolicy.SOURCE) public @interface SafeBrowsingThreat {} - /** The resource was blocked for an unknown reason */ + /** The resource was blocked for an unknown reason. */ public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0; - /** The resource was blocked because it contains malware */ + /** The resource was blocked because it contains malware. */ public static final int SAFE_BROWSING_THREAT_MALWARE = 1; - /** The resource was blocked because it contains deceptive content */ + /** The resource was blocked because it contains deceptive content. */ public static final int SAFE_BROWSING_THREAT_PHISHING = 2; - /** The resource was blocked because it contains unwanted software */ + /** The resource was blocked because it contains unwanted software. */ public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 3; + /** + * The resource was blocked because it may trick the user into a billing agreement. + * + * <p>This constant is only used when targetSdkVersion is at least {@link + * android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used + * instead. + */ + public static final int SAFE_BROWSING_THREAT_BILLING = 4; /** * Report an error to the host application. These errors are unrecoverable diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 8f17e96c144e..4d031232152a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -370,10 +370,12 @@ public class RemoteViews implements Parcelable, Filter { // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? Context context = view.getContext(); ActivityOptions opts = getActivityOptions(context); + // The NEW_TASK flags are applied through the activity options and not as a part of + // the call to startIntentSender() to ensure that they are consistently applied to + // both mutable and immutable PendingIntents. context.startIntentSender( pendingIntent.getIntentSender(), fillInIntent, - Intent.FLAG_ACTIVITY_NEW_TASK, - Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); + 0, 0, 0, opts.toBundle()); } catch (IntentSender.SendIntentException e) { android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); return false; @@ -401,10 +403,15 @@ public class RemoteViews implements Parcelable, Filter { windowAnimationStyle.recycle(); if (enterAnimationId != 0) { - return ActivityOptions.makeCustomAnimation(context, enterAnimationId, 0); + final ActivityOptions opts = ActivityOptions.makeCustomAnimation(context, + enterAnimationId, 0); + opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return opts; } } - return ActivityOptions.makeBasic(); + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return opts; } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f74c2341d816..3dd6fd1410bd 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -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/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java index c029bb20d24c..3ea873bbb0f9 100644 --- a/core/java/com/google/android/collect/Lists.java +++ b/core/java/com/google/android/collect/Lists.java @@ -16,6 +16,7 @@ package com.google.android.collect; +import android.annotation.UnsupportedAppUsage; import java.util.ArrayList; import java.util.Collections; @@ -33,6 +34,7 @@ public class Lists { * * @return a newly-created, initially-empty {@code ArrayList} */ + @UnsupportedAppUsage public static <E> ArrayList<E> newArrayList() { return new ArrayList<E>(); } diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java index fc2c9feb8068..6ba33207631a 100644 --- a/core/java/com/google/android/collect/Maps.java +++ b/core/java/com/google/android/collect/Maps.java @@ -16,6 +16,7 @@ package com.google.android.collect; +import android.annotation.UnsupportedAppUsage; import android.util.ArrayMap; import java.util.HashMap; @@ -29,6 +30,7 @@ public class Maps { * * @return a newly-created, initially-empty {@code HashMap} */ + @UnsupportedAppUsage public static <K, V> HashMap<K, V> newHashMap() { return new HashMap<K, V>(); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index ed6445d92b13..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", @@ -239,6 +240,7 @@ cc_library_shared { shared_libs: [ "libbpf", + "libnetdbpf", "libnetdutils", "libmemtrack", "libandroidfw", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 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/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 261910727db9..bb291e74ce0d 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -23,8 +23,6 @@ #include <hwui/Paint.h> #include <utils/Log.h> -#include <ResourceCache.h> - #include "SkCanvas.h" #include "SkLatticeIter.h" #include "SkRegion.h" @@ -83,12 +81,7 @@ public: static void finalize(JNIEnv* env, jobject, jlong patchHandle) { int8_t* patch = reinterpret_cast<int8_t*>(patchHandle); - if (android::uirenderer::ResourceCache::hasInstance()) { - Res_png_9patch* p = (Res_png_9patch*) patch; - android::uirenderer::ResourceCache::getInstance().destructor(p); - } else { - delete[] patch; - } + delete[] patch; } static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap, diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp new file mode 100644 index 000000000000..4a30babafa49 --- /dev/null +++ b/core/jni/android_opengl_EGL15.cpp @@ -0,0 +1,557 @@ +/* +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> +#include <utils/misc.h> + +#include <assert.h> +#include <EGL/egl.h> + +#include <ui/ANativeObjectBase.h> + +static int initialized = 0; + +// classes from EGL 1.4 +static jclass egldisplayClass; +static jclass eglsurfaceClass; +static jclass eglconfigClass; +static jclass eglcontextClass; +static jclass bufferClass; +static jclass nioAccessClass; + +static jfieldID positionID; +static jfieldID limitID; +static jfieldID elementSizeShiftID; + +static jmethodID getBasePointerID; +static jmethodID getBaseArrayID; +static jmethodID getBaseArrayOffsetID; + +static jmethodID egldisplayGetHandleID; +static jmethodID eglconfigGetHandleID; +static jmethodID eglcontextGetHandleID; +static jmethodID eglsurfaceGetHandleID; + +static jmethodID egldisplayConstructor; +static jmethodID eglcontextConstructor; +static jmethodID eglsurfaceConstructor; +static jmethodID eglconfigConstructor; + +static jobject eglNoContextObject; +static jobject eglNoDisplayObject; +static jobject eglNoSurfaceObject; + +// classes from EGL 1.5 +static jclass eglimageClass; +static jclass eglsyncClass; + +static jmethodID eglimageGetHandleID; +static jmethodID eglsyncGetHandleID; + +static jmethodID eglimageConstructor; +static jmethodID eglsyncConstructor; + +static jobject eglNoImageObject; +static jobject eglNoSyncObject; + +/* Cache method IDs each time the class is loaded. */ + +static void +nativeClassInit(JNIEnv *_env, jclass glImplClass) +{ + // EGL 1.4 Init + jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig"); + eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal); + jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext"); + eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal); + jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay"); + egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal); + jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface"); + eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal); + + eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J"); + eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J"); + egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J"); + eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J"); + + + eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V"); + eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V"); + egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); + eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V"); + + jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT)); + eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject); + jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY)); + eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject); + jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE)); + eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); + + + jclass eglClass = _env->FindClass("android/opengl/EGL15"); + jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); + _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject); + + jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); + _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject); + + jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); + _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject); + + // EGL 1.5 init + jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); + nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); + + jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); + bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, + "getBasePointer", "(Ljava/nio/Buffer;)J"); + getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + + positionID = _env->GetFieldID(bufferClass, "position", "I"); + limitID = _env->GetFieldID(bufferClass, "limit", "I"); + elementSizeShiftID = + _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); + + jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage"); + eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal); + jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync"); + eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal); + + eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); + + eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V"); + eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V"); + + jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;"); + _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject); + + jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;"); + _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject); +} + +static void * +getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset) +{ + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + + position = _env->GetIntField(buffer, positionID); + limit = _env->GetIntField(buffer, limitID); + elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + *remaining = (limit - position) << elementSizeShift; + pointer = _env->CallStaticLongMethod(nioAccessClass, + getBasePointerID, buffer); + if (pointer != 0L) { + *array = NULL; + return reinterpret_cast<void*>(pointer); + } + eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); + + *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, + getBaseArrayID, buffer); + *offset = _env->CallStaticIntMethod(nioAccessClass, + getBaseArrayOffsetID, buffer); + + return NULL; +} + +static void +releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) +{ + _env->ReleasePrimitiveArrayCritical(array, data, + commit ? 0 : JNI_ABORT); +} + +static void * +fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { + if (obj == NULL){ + jniThrowException(_env, "java/lang/IllegalArgumentException", + "Object is set to null."); + } + + jlong handle = _env->CallLongMethod(obj, mid); + return reinterpret_cast<void*>(handle); +} + +static jobject +toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) { + if (cls == eglimageClass && + (EGLImage)handle == EGL_NO_IMAGE) { + return eglNoImageObject; + } + + return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle)); +} + +// -------------------------------------------------------------------------- +/* EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) */ +static jobject +android_eglCreateSync + (JNIEnv *_env, jobject _this, jobject dpy, jint type, jlongArray attrib_list_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + EGLSync _returnValue = (EGLSync) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLAttrib *attrib_list_base = (EGLAttrib *) 0; + jint _remaining; + EGLAttrib *attrib_list = (EGLAttrib *) 0; + + if (!attrib_list_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "attrib_list == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _remaining = _env->GetArrayLength(attrib_list_ref) - offset; + attrib_list_base = (EGLAttrib *) + _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0); + attrib_list = attrib_list_base + offset; + + _returnValue = eglCreateSync( + (EGLDisplay)dpy_native, + (EGLenum)type, + (EGLAttrib *)attrib_list + ); + +exit: + if (attrib_list_base) { + _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base, + JNI_ABORT); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return toEGLHandle(_env, eglsyncClass, eglsyncConstructor, _returnValue); +} + +/* EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) */ +static jboolean +android_eglDestroySync + (JNIEnv *_env, jobject _this, jobject dpy, jobject sync) { + EGLBoolean _returnValue = (EGLBoolean) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync); + + _returnValue = eglDestroySync( + (EGLDisplay)dpy_native, + (EGLSync)sync_native + ); + return (jboolean)_returnValue; +} + +/* EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) */ +static jint +android_eglClientWaitSync + (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags, jlong timeout) { + EGLint _returnValue = (EGLint) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync); + + _returnValue = eglClientWaitSync( + (EGLDisplay)dpy_native, + (EGLSync)sync_native, + (EGLint)flags, + (EGLTime)timeout + ); + return (jint)_returnValue; +} + +/* EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) */ +static jboolean +android_eglGetSyncAttrib + (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint attribute, jlongArray value_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + EGLBoolean _returnValue = (EGLBoolean) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync); + EGLAttrib *value_base = (EGLAttrib *) 0; + jint _remaining; + EGLAttrib *value = (EGLAttrib *) 0; + + if (!value_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "value == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _remaining = _env->GetArrayLength(value_ref) - offset; + value_base = (EGLAttrib *) + _env->GetLongArrayElements(value_ref, (jboolean *)0); + value = value_base + offset; + + _returnValue = eglGetSyncAttrib( + (EGLDisplay)dpy_native, + (EGLSync)sync_native, + (EGLint)attribute, + (EGLAttrib *)value + ); + +exit: + if (value_base) { + _env->ReleaseLongArrayElements(value_ref, (jlong*)value_base, + _exception ? JNI_ABORT: 0); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return (jboolean)_returnValue; +} + +/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */ +static jobject +android_eglGetPlatformDisplay + (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + EGLDisplay _returnValue = (EGLDisplay) 0; + EGLAttrib *attrib_list_base = (EGLAttrib *) 0; + jint _remaining; + EGLAttrib *attrib_list = (EGLAttrib *) 0; + + if (!attrib_list_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "attrib_list == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _remaining = _env->GetArrayLength(attrib_list_ref) - offset; + attrib_list_base = (EGLAttrib *) + _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0); + attrib_list = attrib_list_base + offset; + + _returnValue = eglGetPlatformDisplay( + (EGLenum)platform, + (void *)native_display, + (EGLAttrib *)attrib_list + ); + +exit: + if (attrib_list_base) { + _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base, + JNI_ABORT); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue); +} + +/* EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) */ +static jobject +android_eglCreatePlatformWindowSurface + (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_window_buf, jlongArray attrib_list_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + jarray _array = (jarray) 0; + jint _bufferOffset = (jint) 0; + EGLSurface _returnValue = (EGLSurface) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config); + jint _native_windowRemaining; + void *native_window = (void *) 0; + EGLAttrib *attrib_list_base = (EGLAttrib *) 0; + jint _attrib_listRemaining; + EGLAttrib *attrib_list = (EGLAttrib *) 0; + + if (!native_window_buf) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "native_window == null"; + goto exit; + } + native_window = (void *)getPointer(_env, native_window_buf, (jarray*)&_array, &_native_windowRemaining, &_bufferOffset); + if (!attrib_list_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "attrib_list == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset; + attrib_list_base = (EGLAttrib *) + _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0); + attrib_list = attrib_list_base + offset; + + if (native_window == NULL) { + char * _native_windowBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0); + native_window = (void *) (_native_windowBase + _bufferOffset); + } + _returnValue = eglCreatePlatformWindowSurface( + (EGLDisplay)dpy_native, + (EGLConfig)config_native, + (void *)native_window, + (EGLAttrib *)attrib_list + ); + +exit: + if (attrib_list_base) { + _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base, + JNI_ABORT); + } + if (_array) { + releasePointer(_env, _array, native_window, _exception ? JNI_FALSE : JNI_TRUE); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); +} + +/* EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) */ +static jobject +android_eglCreatePlatformPixmapSurface + (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + jarray _array = (jarray) 0; + jint _bufferOffset = (jint) 0; + EGLSurface _returnValue = (EGLSurface) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config); + jint _native_pixmapRemaining; + void *native_pixmap = (void *) 0; + EGLAttrib *attrib_list_base = (EGLAttrib *) 0; + jint _attrib_listRemaining; + EGLAttrib *attrib_list = (EGLAttrib *) 0; + + if (!native_pixmap_buf) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "native_pixmap == null"; + goto exit; + } + native_pixmap = (void *)getPointer(_env, native_pixmap_buf, (jarray*)&_array, &_native_pixmapRemaining, &_bufferOffset); + if (!attrib_list_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "attrib_list == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset; + attrib_list_base = (EGLAttrib *) + _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0); + attrib_list = attrib_list_base + offset; + + if (native_pixmap == NULL) { + char * _native_pixmapBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0); + native_pixmap = (void *) (_native_pixmapBase + _bufferOffset); + } + _returnValue = eglCreatePlatformPixmapSurface( + (EGLDisplay)dpy_native, + (EGLConfig)config_native, + (void *)native_pixmap, + (EGLAttrib *)attrib_list + ); + +exit: + if (attrib_list_base) { + _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base, + JNI_ABORT); + } + if (_array) { + releasePointer(_env, _array, native_pixmap, _exception ? JNI_FALSE : JNI_TRUE); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); +} + +/* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */ +static jboolean +android_eglWaitSync + (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags) { + EGLBoolean _returnValue = (EGLBoolean) 0; + EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy); + EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync); + + _returnValue = eglWaitSync( + (EGLDisplay)dpy_native, + (EGLSync)sync_native, + (EGLint)flags + ); + return (jboolean)_returnValue; +} + +static const char *classPathName = "android/opengl/EGL15"; + +static const JNINativeMethod methods[] = { +{"_nativeClassInit", "()V", (void*)nativeClassInit }, +{"eglCreateSync", "(Landroid/opengl/EGLDisplay;I[JI)Landroid/opengl/EGLSync;", (void *) android_eglCreateSync }, +{"eglDestroySync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)Z", (void *) android_eglDestroySync }, +{"eglClientWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;IJ)I", (void *) android_eglClientWaitSync }, +{"eglGetSyncAttrib", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I[JI)Z", (void *) android_eglGetSyncAttrib }, +{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay }, +{"eglCreatePlatformWindowSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformWindowSurface }, +{"eglCreatePlatformPixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformPixmapSurface }, +{"eglWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I)Z", (void *) android_eglWaitSync }, +}; + +int register_android_opengl_jni_EGL15(JNIEnv *_env) +{ + int err; + err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods)); + return err; +} diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 92235ad494a7..e64da5ca0b24 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -32,12 +32,16 @@ void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str()); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) { +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn, + jobject rulesFd, jlong rulesOffset, jlong rulesLength) { ScopedUtfChars pathChars(env, path); ScopedUtfChars appNameChars(env, appName); ScopedUtfChars appPrefChars(env, appPref); + + int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd); + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(), - appPrefChars.c_str(), devOptIn); + appPrefChars.c_str(), devOptIn, rulesFd_native, rulesOffset, rulesLength); } void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) { @@ -57,7 +61,7 @@ void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) }, { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, - { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) }, + { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) }, }; diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp index 109e65c4a1d0..b3ff4dbf8bef 100644 --- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp +++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp @@ -32,8 +32,8 @@ #include <utils/misc.h> #include "android-base/unique_fd.h" -#include "bpf/BpfNetworkStats.h" #include "bpf/BpfUtils.h" +#include "netdbpf/BpfNetworkStats.h" using android::bpf::hasBpfSupport; using android::bpf::parseBpfNetworkStatsDetail; diff --git a/core/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/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 80281b63c4a1..6966448f7d63 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -41,6 +41,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; import android.os.FileUtils.MemoryPipe; @@ -510,6 +511,20 @@ public class FileUtilsTest { MODE_WRITE_ONLY | MODE_CREATE | MODE_APPEND); } + @Test + public void testTranslateMode_Invalid() throws Exception { + try { + translateModeStringToPosix("rwx"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + translateModeStringToPosix(""); + fail(); + } catch (IllegalArgumentException expected) { + } + } + private static void assertTranslate(String string, int posix, int pfd) { assertEquals(posix, translateModeStringToPosix(string)); assertEquals(string, translateModePosixToString(posix)); diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 632c37fd54d2..9778acba8213 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -368,6 +368,8 @@ public class SettingsBackupTest { Settings.Global.PRIV_APP_OOB_ENABLED, Settings.Global.PRIV_APP_OOB_LIST, Settings.Global.PRIVATE_DNS_DEFAULT_MODE, + Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, + Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS, Settings.Global.RADIO_BLUETOOTH, Settings.Global.RADIO_CELL, diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java index c98e6460c189..7360e9f7d6f6 100644 --- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java +++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.Suppress; import android.text.InputType; import android.util.KeyUtils; import android.view.KeyEvent; @@ -239,7 +238,6 @@ public class ForwardDeleteTest { } @Test - @Suppress public void testEmojiModifier() { EditorState state = new EditorState(); @@ -256,20 +254,15 @@ public class ForwardDeleteTest { // Isolated multiple emoji modifier state.setByString("| U+1F3FB U+1F3FB"); forwardDelete(state, 0); - state.assertEquals("| U+1F3FB"); - forwardDelete(state, 0); state.assertEquals("|"); // Multiple emoji modifiers state.setByString("| U+1F466 U+1F3FB U+1F3FB"); forwardDelete(state, 0); - state.assertEquals("| U+1F3FB"); - forwardDelete(state, 0); state.assertEquals("|"); } @Test - @Suppress public void testMixedEdgeCases() { EditorState state = new EditorState(); @@ -318,7 +311,7 @@ public class ForwardDeleteTest { // COMBINING ENCLOSING KEYCAP + emoji modifier state.setByString("| '1' U+20E3 U+1F3FB"); forwardDelete(state, 0); - state.assertEquals("| U+1F3FB"); + state.assertEquals("|"); // Emoji modifier + COMBINING ENCLOSING KEYCAP state.setByString("| U+1F466 U+1F3FB U+20E3"); @@ -360,7 +353,7 @@ public class ForwardDeleteTest { // Variation selector + emoji modifier state.setByString("| U+2665 U+FE0F U+1F3FB"); forwardDelete(state, 0); - state.assertEquals("| U+1F3FB"); + state.assertEquals("|"); // Emoji modifier + variation selector state.setByString("| U+1F466 U+1F3FB U+FE0F"); @@ -396,7 +389,7 @@ public class ForwardDeleteTest { // Start with ZERO WIDTH JOINER + emoji modifier state.setByString("| U+200D U+1F3FB"); forwardDelete(state, 0); - state.assertEquals("| U+1F3FB"); + state.assertEquals("|"); // ZERO WIDTH JOINER + emoji modifier state.setByString("| U+1F469 U+200D U+1F3FB"); @@ -418,8 +411,6 @@ public class ForwardDeleteTest { // Regional indicator symbol + emoji modifier state.setByString("| U+1F1FA U+1F3FB"); forwardDelete(state, 0); - state.assertEquals("| U+1F3FB"); - forwardDelete(state, 0); state.assertEquals("|"); // Emoji modifier + regional indicator symbol diff --git a/core/tests/coretests/src/android/view/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/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/Android.bp b/libs/hwui/Android.bp index e3ec45b20883..503951d1adc6 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -202,9 +202,7 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "CanvasState.cpp", "CanvasTransform.cpp", - "ClipArea.cpp", "DamageAccumulator.cpp", "DeferredLayerUpdater.cpp", "DeviceInfo.cpp", @@ -227,9 +225,7 @@ cc_defaults { "RecordingCanvas.cpp", "RenderNode.cpp", "RenderProperties.cpp", - "ResourceCache.cpp", "SkiaCanvas.cpp", - "Snapshot.cpp", "TreeInfo.cpp", "VectorDrawable.cpp", "protos/graphicsstats.proto", @@ -308,8 +304,6 @@ cc_test { "tests/unit/main.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", - "tests/unit/CanvasStateTests.cpp", - "tests/unit/ClipAreaTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", "tests/unit/FatVectorTests.cpp", @@ -328,7 +322,6 @@ cc_test { "tests/unit/SkiaPipelineTests.cpp", "tests/unit/SkiaRenderPropertiesTests.cpp", "tests/unit/SkiaCanvasTests.cpp", - "tests/unit/SnapshotTests.cpp", "tests/unit/StringUtilsTests.cpp", "tests/unit/TestUtilsTests.cpp", "tests/unit/ThreadBaseTests.cpp", diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp deleted file mode 100644 index d18c4abde7f2..000000000000 --- a/libs/hwui/CanvasState.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CanvasState.h" -#include "hwui/Canvas.h" -#include "utils/MathUtils.h" - -namespace android { -namespace uirenderer { - -CanvasState::CanvasState(CanvasStateClient& renderer) - : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {} - -CanvasState::~CanvasState() { - // First call freeSnapshot on all but mFirstSnapshot - // to invoke all the dtors - freeAllSnapshots(); - - // Now actually release the memory - while (mSnapshotPool) { - void* temp = mSnapshotPool; - mSnapshotPool = mSnapshotPool->previous; - free(temp); - } -} - -void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) { - if (mWidth != viewportWidth || mHeight != viewportHeight) { - mWidth = viewportWidth; - mHeight = viewportHeight; - mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); - mCanvas.onViewportInitialized(); - } - - freeAllSnapshots(); - mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); - mSnapshot->setRelativeLightCenter(Vector3()); - mSaveCount = 1; -} - -void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, - float clipTop, float clipRight, float clipBottom, - const Vector3& lightCenter) { - if (mWidth != viewportWidth || mHeight != viewportHeight) { - mWidth = viewportWidth; - mHeight = viewportHeight; - mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); - mCanvas.onViewportInitialized(); - } - - freeAllSnapshots(); - mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); - mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); - mSnapshot->fbo = mCanvas.getTargetFbo(); - mSnapshot->setRelativeLightCenter(lightCenter); - mSaveCount = 1; -} - -Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) { - void* memory; - if (mSnapshotPool) { - memory = mSnapshotPool; - mSnapshotPool = mSnapshotPool->previous; - mSnapshotPoolCount--; - } else { - memory = malloc(sizeof(Snapshot)); - } - return new (memory) Snapshot(previous, savecount); -} - -void CanvasState::freeSnapshot(Snapshot* snapshot) { - snapshot->~Snapshot(); - // Arbitrary number, just don't let this grown unbounded - if (mSnapshotPoolCount > 10) { - free((void*)snapshot); - } else { - snapshot->previous = mSnapshotPool; - mSnapshotPool = snapshot; - mSnapshotPoolCount++; - } -} - -void CanvasState::freeAllSnapshots() { - while (mSnapshot != &mFirstSnapshot) { - Snapshot* temp = mSnapshot; - mSnapshot = mSnapshot->previous; - freeSnapshot(temp); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Save (layer) -/////////////////////////////////////////////////////////////////////////////// - -/** - * Guaranteed to save without side-effects - * - * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save - * stack, and ensures restoreToCount() doesn't call back into subclass overrides. - */ -int CanvasState::saveSnapshot(int flags) { - mSnapshot = allocSnapshot(mSnapshot, flags); - return mSaveCount++; -} - -int CanvasState::save(int flags) { - return saveSnapshot(flags); -} - -/** - * Guaranteed to restore without side-effects. - */ -void CanvasState::restoreSnapshot() { - Snapshot* toRemove = mSnapshot; - Snapshot* toRestore = mSnapshot->previous; - - mSaveCount--; - mSnapshot = toRestore; - - // subclass handles restore implementation - mCanvas.onSnapshotRestored(*toRemove, *toRestore); - - freeSnapshot(toRemove); -} - -void CanvasState::restore() { - if (mSaveCount > 1) { - restoreSnapshot(); - } -} - -void CanvasState::restoreToCount(int saveCount) { - if (saveCount < 1) saveCount = 1; - - while (mSaveCount > saveCount) { - restoreSnapshot(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Matrix -/////////////////////////////////////////////////////////////////////////////// - -void CanvasState::getMatrix(SkMatrix* matrix) const { - mSnapshot->transform->copyTo(*matrix); -} - -void CanvasState::translate(float dx, float dy, float dz) { - mSnapshot->transform->translate(dx, dy, dz); -} - -void CanvasState::rotate(float degrees) { - mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); -} - -void CanvasState::scale(float sx, float sy) { - mSnapshot->transform->scale(sx, sy, 1.0f); -} - -void CanvasState::skew(float sx, float sy) { - mSnapshot->transform->skew(sx, sy); -} - -void CanvasState::setMatrix(const SkMatrix& matrix) { - mSnapshot->transform->load(matrix); -} - -void CanvasState::setMatrix(const Matrix4& matrix) { - *(mSnapshot->transform) = matrix; -} - -void CanvasState::concatMatrix(const SkMatrix& matrix) { - mat4 transform(matrix); - mSnapshot->transform->multiply(transform); -} - -void CanvasState::concatMatrix(const Matrix4& matrix) { - mSnapshot->transform->multiply(matrix); -} - -/////////////////////////////////////////////////////////////////////////////// -// Clip -/////////////////////////////////////////////////////////////////////////////// - -bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) { - mSnapshot->clip(Rect(left, top, right, bottom), op); - return !mSnapshot->clipIsEmpty(); -} - -bool CanvasState::clipPath(const SkPath* path, SkClipOp op) { - mSnapshot->clipPath(*path, op); - return !mSnapshot->clipIsEmpty(); -} - -void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { - Rect bounds; - float radius; - if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported - - bool outlineIsRounded = MathUtils::isPositive(radius); - if (!outlineIsRounded || currentTransform()->isSimple()) { - // TODO: consider storing this rect separately, so that this can't be replaced with clip ops - clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect); - } - if (outlineIsRounded) { - setClippingRoundRect(allocator, bounds, radius, false); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Quick Rejection -/////////////////////////////////////////////////////////////////////////////// - -/** - * Calculates whether content drawn within the passed bounds would be outside of, or intersect with - * the clipRect. Does not modify the scissor. - * - * @param clipRequired if not null, will be set to true if element intersects clip - * (and wasn't rejected) - * - * @param snapOut if set, the geometry will be treated as having an AA ramp. - * See Rect::snapGeometryToPixelBoundaries() - */ -bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, - bool snapOut) const { - if (bottom <= top || right <= left) { - return true; - } - - Rect r(left, top, right, bottom); - currentTransform()->mapRect(r); - r.snapGeometryToPixelBoundaries(snapOut); - - Rect clipRect(currentRenderTargetClip()); - clipRect.snapToPixelBoundaries(); - - if (!clipRect.intersects(r)) return true; - - // clip is required if geometry intersects clip rect - if (clipRequired) { - *clipRequired = !clipRect.contains(r); - } - - // round rect clip is required if RR clip exists, and geometry intersects its corners - if (roundRectClipRequired) { - *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr && - mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); - } - return false; -} - -bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const { - if (bottom <= top || right <= left) { - return true; - } - - Rect r(left, top, right, bottom); - currentTransform()->mapRect(r); - r.roundOut(); // rounded out to be conservative - - Rect clipRect(currentRenderTargetClip()); - clipRect.snapToPixelBoundaries(); - - if (!clipRect.intersects(r)) return true; - - return false; -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h deleted file mode 100644 index 9ac35ff47dab..000000000000 --- a/libs/hwui/CanvasState.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "Snapshot.h" - -#include <SkClipOp.h> -#include <SkMatrix.h> -#include <SkPath.h> -#include <SkRegion.h> - -namespace android { -namespace uirenderer { - -/** - * Abstract base class for any class containing CanvasState. - * Defines three mandatory callbacks. - */ -class CanvasStateClient { -public: - CanvasStateClient() {} - virtual ~CanvasStateClient() {} - - /** - * Callback allowing embedder to take actions in the middle of a - * setViewport() call. - */ - virtual void onViewportInitialized() = 0; - - /** - * Callback allowing embedder to take actions in the middle of a - * restore() call. May be called several times sequentially. - */ - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0; - - /** - * Allows subclasses to control what value is stored in snapshot's - * fbo field in * initializeSaveStack. - */ - virtual GLuint getTargetFbo() const = 0; - -}; // class CanvasStateClient - -/** - * Implements Canvas state methods on behalf of Renderers. - * - * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the - * Renderer interface. Drawing and recording classes that include a CanvasState will have - * different use cases: - * - * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as - * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating - * the stack itself. - * - * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass - * through state operations to CanvasState, so that not only will querying operations work - * (getClip/Matrix), but so that quickRejection can also be used. - */ - -class CanvasState { -public: - explicit CanvasState(CanvasStateClient& renderer); - ~CanvasState(); - - /** - * Initializes the first snapshot, computing the projection matrix, - * and stores the dimensions of the render target. - */ - void initializeRecordingSaveStack(int viewportWidth, int viewportHeight); - - /** - * Initializes the first snapshot, computing the projection matrix, - * and stores the dimensions of the render target. - */ - void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop, - float clipRight, float clipBottom, const Vector3& lightCenter); - - bool hasRectToRectTransform() const { return CC_LIKELY(currentTransform()->rectToRect()); } - - // Save (layer) - int getSaveCount() const { return mSaveCount; } - int save(int flags); - void restore(); - void restoreToCount(int saveCount); - - // Save/Restore without side-effects - int saveSnapshot(int flags); - void restoreSnapshot(); - - // Matrix - void getMatrix(SkMatrix* outMatrix) const; - void translate(float dx, float dy, float dz = 0.0f); - void rotate(float degrees); - void scale(float sx, float sy); - void skew(float sx, float sy); - - void setMatrix(const SkMatrix& matrix); - void setMatrix(const Matrix4& matrix); // internal only convenience method - void concatMatrix(const SkMatrix& matrix); - void concatMatrix(const Matrix4& matrix); // internal only convenience method - - // Clip - const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); } - const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } - - bool quickRejectConservative(float left, float top, float right, float bottom) const; - - bool clipRect(float left, float top, float right, float bottom, SkClipOp op); - bool clipPath(const SkPath* path, SkClipOp op); - - /** - * Sets a "clipping outline", which is independent from the regular clip. - * Currently only supports rectangles or rounded rectangles; passing in a - * more complicated outline fails silently. Replaces any previous clipping - * outline. - */ - void setClippingOutline(LinearAllocator& allocator, const Outline* outline); - void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius, - bool highPriority = true) { - mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); - } - void setProjectionPathMask(const SkPath* path) { mSnapshot->setProjectionPathMask(path); } - - /** - * Returns true if drawing in the rectangle (left, top, right, bottom) - * will be clipped out. Is conservative: might return false when subpixel- - * perfect tests would return true. - */ - bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, - bool snapOut) const; - - void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; } - - inline const mat4* currentTransform() const { return currentSnapshot()->transform; } - inline const Rect& currentRenderTargetClip() const { - return currentSnapshot()->getRenderTargetClip(); - } - inline int currentFlags() const { return currentSnapshot()->flags; } - const Vector3& currentLightCenter() const { - return currentSnapshot()->getRelativeLightCenter(); - } - int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); } - int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); } - int getWidth() const { return mWidth; } - int getHeight() const { return mHeight; } - bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); } - - inline const Snapshot* currentSnapshot() const { return mSnapshot; } - inline Snapshot* writableSnapshot() { return mSnapshot; } - inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; } - -private: - Snapshot* allocSnapshot(Snapshot* previous, int savecount); - void freeSnapshot(Snapshot* snapshot); - void freeAllSnapshots(); - - /// Dimensions of the drawing surface - int mWidth, mHeight; - - /// Number of saved states - int mSaveCount; - - /// Base state - Snapshot mFirstSnapshot; - - /// Host providing callbacks - CanvasStateClient& mCanvas; - - /// Current state - Snapshot* mSnapshot; - - // Pool of allocated snapshots to re-use - // NOTE: The dtors have already been invoked! - Snapshot* mSnapshotPool = nullptr; - int mSnapshotPoolCount = 0; - -}; // class CanvasState - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp deleted file mode 100644 index 27d93cfa0391..000000000000 --- a/libs/hwui/ClipArea.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClipArea.h" - -#include "utils/LinearAllocator.h" - -#include <SkPath.h> -#include <limits> -#include <type_traits> - -namespace android { -namespace uirenderer { - -static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { - Vertex v = {x, y}; - transform.mapPoint(v.x, v.y); - transformedBounds.expandToCover(v.x, v.y); -} - -Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) { - const float kMinFloat = std::numeric_limits<float>::lowest(); - const float kMaxFloat = std::numeric_limits<float>::max(); - Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat}; - handlePoint(transformedBounds, transform, r.left, r.top); - handlePoint(transformedBounds, transform, r.right, r.top); - handlePoint(transformedBounds, transform, r.left, r.bottom); - handlePoint(transformedBounds, transform, r.right, r.bottom); - return transformedBounds; -} - -void ClipBase::dump() const { - ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect)); -} - -/* - * TransformedRectangle - */ - -TransformedRectangle::TransformedRectangle() {} - -TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform) - : mBounds(bounds), mTransform(transform) {} - -bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const { - return mTransform == other.mTransform; -} - -void TransformedRectangle::intersectWith(const TransformedRectangle& other) { - mBounds.doIntersect(other.mBounds); -} - -bool TransformedRectangle::isEmpty() const { - return mBounds.isEmpty(); -} - -/* - * RectangleList - */ - -RectangleList::RectangleList() : mTransformedRectanglesCount(0) {} - -bool RectangleList::isEmpty() const { - if (mTransformedRectanglesCount < 1) { - return true; - } - - for (int i = 0; i < mTransformedRectanglesCount; i++) { - if (mTransformedRectangles[i].isEmpty()) { - return true; - } - } - return false; -} - -int RectangleList::getTransformedRectanglesCount() const { - return mTransformedRectanglesCount; -} - -const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const { - return mTransformedRectangles[i]; -} - -void RectangleList::setEmpty() { - mTransformedRectanglesCount = 0; -} - -void RectangleList::set(const Rect& bounds, const Matrix4& transform) { - mTransformedRectanglesCount = 1; - mTransformedRectangles[0] = TransformedRectangle(bounds, transform); -} - -bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) { - TransformedRectangle newRectangle(bounds, transform); - - // Try to find a rectangle with a compatible transformation - int index = 0; - for (; index < mTransformedRectanglesCount; index++) { - TransformedRectangle& tr(mTransformedRectangles[index]); - if (tr.canSimplyIntersectWith(newRectangle)) { - tr.intersectWith(newRectangle); - return true; - } - } - - // Add it to the list if there is room - if (index < kMaxTransformedRectangles) { - mTransformedRectangles[index] = newRectangle; - mTransformedRectanglesCount += 1; - return true; - } - - // This rectangle list is full - return false; -} - -Rect RectangleList::calculateBounds() const { - Rect bounds; - for (int index = 0; index < mTransformedRectanglesCount; index++) { - const TransformedRectangle& tr(mTransformedRectangles[index]); - if (index == 0) { - bounds = tr.transformedBounds(); - } else { - bounds.doIntersect(tr.transformedBounds()); - } - } - return bounds; -} - -static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) { - SkPath rectPath; - SkPath rectPathTransformed; - rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom); - SkMatrix skTransform; - transform.copyTo(skTransform); - rectPath.transform(skTransform, &rectPathTransformed); - return rectPathTransformed; -} - -SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { - SkRegion rectangleListAsRegion; - for (int index = 0; index < mTransformedRectanglesCount; index++) { - const TransformedRectangle& tr(mTransformedRectangles[index]); - SkPath rectPathTransformed = - pathFromTransformedRectangle(tr.getBounds(), tr.getTransform()); - if (index == 0) { - rectangleListAsRegion.setPath(rectPathTransformed, clip); - } else { - SkRegion rectRegion; - rectRegion.setPath(rectPathTransformed, clip); - rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op); - } - } - return rectangleListAsRegion; -} - -void RectangleList::transform(const Matrix4& transform) { - for (int index = 0; index < mTransformedRectanglesCount; index++) { - mTransformedRectangles[index].transform(transform); - } -} - -/* - * ClipArea - */ - -ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {} - -/* - * Interface - */ - -void ClipArea::setViewportDimensions(int width, int height) { - mPostViewportClipObserved = false; - mViewportBounds.set(0, 0, width, height); - mClipRect = mViewportBounds; -} - -void ClipArea::setEmpty() { - onClipUpdated(); - mMode = ClipMode::Rectangle; - mClipRect.setEmpty(); - mClipRegion.setEmpty(); - mRectangleList.setEmpty(); -} - -void ClipArea::setClip(float left, float top, float right, float bottom) { - onClipUpdated(); - mMode = ClipMode::Rectangle; - mClipRect.set(left, top, right, bottom); - mClipRegion.setEmpty(); -} - -void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - switch (mMode) { - case ClipMode::Rectangle: - rectangleModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::RectangleList: - rectangleListModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::Region: - regionModeClipRectWithTransform(r, transform, op); - break; - } -} - -void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - enterRegionMode(); - mClipRegion.op(region, op); - onClipRegionUpdated(); -} - -void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - SkMatrix skTransform; - transform->copyTo(skTransform); - SkPath transformed; - path.transform(skTransform, &transformed); - SkRegion region; - regionFromPath(transformed, region); - enterRegionMode(); - mClipRegion.op(region, op); - onClipRegionUpdated(); -} - -/* - * Rectangle mode - */ - -void ClipArea::enterRectangleMode() { - // Entering rectangle mode discards any - // existing clipping information from the other modes. - // The only way this occurs is by a clip setting operation. - mMode = ClipMode::Rectangle; -} - -void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - if (op == SkRegion::kReplace_Op && transform->rectToRect()) { - mClipRect = r; - transform->mapRect(mClipRect); - return; - } else if (op != SkRegion::kIntersect_Op) { - enterRegionMode(); - regionModeClipRectWithTransform(r, transform, op); - return; - } - - if (transform->rectToRect()) { - Rect transformed(r); - transform->mapRect(transformed); - mClipRect.doIntersect(transformed); - return; - } - - enterRectangleListMode(); - rectangleListModeClipRectWithTransform(r, transform, op); -} - -/* - * RectangleList mode implementation - */ - -void ClipArea::enterRectangleListMode() { - // Is is only legal to enter rectangle list mode from - // rectangle mode, since rectangle list mode cannot represent - // all clip areas that can be represented by a region. - ALOG_ASSERT(mMode == ClipMode::Rectangle); - mMode = ClipMode::RectangleList; - mRectangleList.set(mClipRect, Matrix4::identity()); -} - -void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) { - enterRegionMode(); - regionModeClipRectWithTransform(r, transform, op); - } -} - -/* - * Region mode implementation - */ - -void ClipArea::enterRegionMode() { - ClipMode oldMode = mMode; - mMode = ClipMode::Region; - if (oldMode != ClipMode::Region) { - if (oldMode == ClipMode::Rectangle) { - mClipRegion.setRect(mClipRect.toSkIRect()); - } else { - mClipRegion = mRectangleList.convertToRegion(createViewportRegion()); - onClipRegionUpdated(); - } - } -} - -void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - SkPath transformedRect = pathFromTransformedRectangle(r, *transform); - SkRegion transformedRectRegion; - regionFromPath(transformedRect, transformedRectRegion); - mClipRegion.op(transformedRectRegion, op); - onClipRegionUpdated(); -} - -void ClipArea::onClipRegionUpdated() { - if (!mClipRegion.isEmpty()) { - mClipRect.set(mClipRegion.getBounds()); - - if (mClipRegion.isRect()) { - mClipRegion.setEmpty(); - enterRectangleMode(); - } - } else { - mClipRect.setEmpty(); - } -} - -/** - * Clip serialization - */ - -const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) { - if (!mPostViewportClipObserved) { - // Only initial clip-to-viewport observed, so no serialization of clip necessary - return nullptr; - } - - static_assert(std::is_trivially_destructible<Rect>::value, - "expect Rect to be trivially destructible"); - static_assert(std::is_trivially_destructible<RectangleList>::value, - "expect RectangleList to be trivially destructible"); - - if (mLastSerialization == nullptr) { - ClipBase* serialization = nullptr; - switch (mMode) { - case ClipMode::Rectangle: - serialization = allocator.create<ClipRect>(mClipRect); - break; - case ClipMode::RectangleList: - serialization = allocator.create<ClipRectList>(mRectangleList); - serialization->rect = mRectangleList.calculateBounds(); - break; - case ClipMode::Region: - serialization = allocator.create<ClipRegion>(mClipRegion); - serialization->rect.set(mClipRegion.getBounds()); - break; - } - serialization->intersectWithRoot = mReplaceOpObserved; - // TODO: this is only done for draw time, should eventually avoid for record time - serialization->rect.snapToPixelBoundaries(); - mLastSerialization = serialization; - } - return mLastSerialization; -} - -inline static const RectangleList& getRectList(const ClipBase* scb) { - return reinterpret_cast<const ClipRectList*>(scb)->rectList; -} - -inline static const SkRegion& getRegion(const ClipBase* scb) { - return reinterpret_cast<const ClipRegion*>(scb)->region; -} - -// Conservative check for too many rectangles to fit in rectangle list. -// For simplicity, doesn't account for rect merging -static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) { - int currentRectCount = clipArea.isRectangleList() - ? clipArea.getRectangleList().getTransformedRectanglesCount() - : 1; - int recordedRectCount = (scb->mode == ClipMode::RectangleList) - ? getRectList(scb).getTransformedRectanglesCount() - : 1; - return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles; -} - -static const ClipRect sEmptyClipRect(Rect(0, 0)); - -const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, - const Matrix4& recordedClipTransform) { - // if no recordedClip passed, just serialize current state - if (!recordedClip) return serializeClip(allocator); - - // if either is empty, clip is empty - if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect; - - if (!mLastResolutionResult || recordedClip != mLastResolutionClip || - recordedClipTransform != mLastResolutionTransform) { - mLastResolutionClip = recordedClip; - mLastResolutionTransform = recordedClipTransform; - - if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle && - recordedClipTransform.rectToRect())) { - // common case - result is a single rectangle - auto rectClip = allocator.create<ClipRect>(recordedClip->rect); - recordedClipTransform.mapRect(rectClip->rect); - rectClip->rect.doIntersect(mClipRect); - rectClip->rect.snapToPixelBoundaries(); - mLastResolutionResult = rectClip; - } else if (CC_UNLIKELY(mMode == ClipMode::Region || - recordedClip->mode == ClipMode::Region || - cannotFitInRectangleList(*this, recordedClip))) { - // region case - SkRegion other; - switch (recordedClip->mode) { - case ClipMode::Rectangle: - if (CC_LIKELY(recordedClipTransform.rectToRect())) { - // simple transform, skip creating SkPath - Rect resultClip(recordedClip->rect); - recordedClipTransform.mapRect(resultClip); - other.setRect(resultClip.toSkIRect()); - } else { - SkPath transformedRect = pathFromTransformedRectangle( - recordedClip->rect, recordedClipTransform); - other.setPath(transformedRect, createViewportRegion()); - } - break; - case ClipMode::RectangleList: { - RectangleList transformedList(getRectList(recordedClip)); - transformedList.transform(recordedClipTransform); - other = transformedList.convertToRegion(createViewportRegion()); - break; - } - case ClipMode::Region: - other = getRegion(recordedClip); - applyTransformToRegion(recordedClipTransform, &other); - } - - ClipRegion* regionClip = allocator.create<ClipRegion>(); - switch (mMode) { - case ClipMode::Rectangle: - regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op); - break; - case ClipMode::RectangleList: - regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()), - other, SkRegion::kIntersect_Op); - break; - case ClipMode::Region: - regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op); - break; - } - // Don't need to snap, since region's in int bounds - regionClip->rect.set(regionClip->region.getBounds()); - mLastResolutionResult = regionClip; - } else { - auto rectListClip = allocator.create<ClipRectList>(mRectangleList); - auto&& rectList = rectListClip->rectList; - if (mMode == ClipMode::Rectangle) { - rectList.set(mClipRect, Matrix4::identity()); - } - - if (recordedClip->mode == ClipMode::Rectangle) { - rectList.intersectWith(recordedClip->rect, recordedClipTransform); - } else { - const RectangleList& other = getRectList(recordedClip); - for (int i = 0; i < other.getTransformedRectanglesCount(); i++) { - auto&& tr = other.getTransformedRectangle(i); - Matrix4 totalTransform(recordedClipTransform); - totalTransform.multiply(tr.getTransform()); - rectList.intersectWith(tr.getBounds(), totalTransform); - } - } - rectListClip->rect = rectList.calculateBounds(); - rectListClip->rect.snapToPixelBoundaries(); - mLastResolutionResult = rectListClip; - } - } - return mLastResolutionResult; -} - -void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) { - if (!clip) return; // nothing to do - - if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) { - clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op); - } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) { - auto&& rectList = getRectList(clip); - for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) { - auto&& tr = rectList.getTransformedRectangle(i); - Matrix4 totalTransform(transform); - totalTransform.multiply(tr.getTransform()); - clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op); - } - } else { - SkRegion region(getRegion(clip)); - applyTransformToRegion(transform, ®ion); - clipRegion(region, SkRegion::kIntersect_Op); - } -} - -void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) { - if (transform.rectToRect() && !transform.isPureTranslate()) { - // handle matrices with scale manually by mapping each rect - SkRegion other; - SkRegion::Iterator it(*region); - while (!it.done()) { - Rect rect(it.rect()); - transform.mapRect(rect); - rect.snapGeometryToPixelBoundaries(true); - other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op); - it.next(); - } - region->swap(other); - } else { - // TODO: handle non-translate transforms properly! - region->translate(transform.getTranslateX(), transform.getTranslateY()); - } -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h deleted file mode 100644 index a7a11801cfe2..000000000000 --- a/libs/hwui/ClipArea.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef CLIPAREA_H -#define CLIPAREA_H - -#include "Matrix.h" -#include "Rect.h" -#include "utils/Pair.h" - -#include <SkRegion.h> - -namespace android { -namespace uirenderer { - -class LinearAllocator; - -Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform); - -class TransformedRectangle { -public: - TransformedRectangle(); - TransformedRectangle(const Rect& bounds, const Matrix4& transform); - - bool canSimplyIntersectWith(const TransformedRectangle& other) const; - void intersectWith(const TransformedRectangle& other); - - bool isEmpty() const; - - const Rect& getBounds() const { return mBounds; } - - Rect transformedBounds() const { - Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform)); - return transformedBounds; - } - - const Matrix4& getTransform() const { return mTransform; } - - void transform(const Matrix4& transform) { - Matrix4 t; - t.loadMultiply(transform, mTransform); - mTransform = t; - } - -private: - Rect mBounds; - Matrix4 mTransform; -}; - -class RectangleList { -public: - RectangleList(); - - bool isEmpty() const; - int getTransformedRectanglesCount() const; - const TransformedRectangle& getTransformedRectangle(int i) const; - - void setEmpty(); - void set(const Rect& bounds, const Matrix4& transform); - bool intersectWith(const Rect& bounds, const Matrix4& transform); - void transform(const Matrix4& transform); - - SkRegion convertToRegion(const SkRegion& clip) const; - Rect calculateBounds() const; - - enum { kMaxTransformedRectangles = 5 }; - -private: - int mTransformedRectanglesCount; - TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles]; -}; - -enum class ClipMode { - Rectangle, - RectangleList, - - // region and path - intersected. if either is empty, don't use - Region -}; - -struct ClipBase { - explicit ClipBase(ClipMode mode) : mode(mode) {} - explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {} - const ClipMode mode; - bool intersectWithRoot = false; - // Bounds of the clipping area, used to define the scissor, and define which - // portion of the stencil is updated/used - Rect rect; - - void dump() const; -}; - -struct ClipRect : ClipBase { - explicit ClipRect(const Rect& rect) : ClipBase(rect) {} -}; - -struct ClipRectList : ClipBase { - explicit ClipRectList(const RectangleList& rectList) - : ClipBase(ClipMode::RectangleList), rectList(rectList) {} - RectangleList rectList; -}; - -struct ClipRegion : ClipBase { - explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {} - ClipRegion() : ClipBase(ClipMode::Region) {} - SkRegion region; -}; - -class ClipArea { -public: - ClipArea(); - - void setViewportDimensions(int width, int height); - - bool isEmpty() const { return mClipRect.isEmpty(); } - - void setEmpty(); - void setClip(float left, float top, float right, float bottom); - void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op); - - const Rect& getClipRect() const { return mClipRect; } - - const SkRegion& getClipRegion() const { return mClipRegion; } - - const RectangleList& getRectangleList() const { return mRectangleList; } - - bool isRegion() const { return ClipMode::Region == mMode; } - - bool isSimple() const { return mMode == ClipMode::Rectangle; } - - bool isRectangleList() const { return mMode == ClipMode::RectangleList; } - - WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator); - WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip( - LinearAllocator& allocator, const ClipBase* recordedClip, - const Matrix4& recordedClipTransform); - void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform); - - static void applyTransformToRegion(const Matrix4& transform, SkRegion* region); - -private: - void enterRectangleMode(); - void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - - void enterRectangleListMode(); - void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op); - - void enterRegionModeFromRectangleMode(); - void enterRegionModeFromRectangleListMode(); - void enterRegionMode(); - void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - - void clipRegion(const SkRegion& region, SkRegion::Op op); - void ensureClipRegion(); - void onClipRegionUpdated(); - - // Called by every state modifying public method. - void onClipUpdated() { - mPostViewportClipObserved = true; - mLastSerialization = nullptr; - mLastResolutionResult = nullptr; - } - - SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); } - - void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) { - // TODO: this should not mask every path to the viewport - this makes it impossible to use - // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op) - pathAsRegion.setPath(path, createViewportRegion()); - } - - ClipMode mMode; - bool mPostViewportClipObserved = false; - bool mReplaceOpObserved = false; - - /** - * If mLastSerialization is non-null, it represents an already serialized copy - * of the current clip state. If null, it has not been computed. - */ - const ClipBase* mLastSerialization = nullptr; - - /** - * This pair of pointers is a single entry cache of most recently seen - */ - const ClipBase* mLastResolutionResult = nullptr; - const ClipBase* mLastResolutionClip = nullptr; - Matrix4 mLastResolutionTransform; - - Rect mViewportBounds; - Rect mClipRect; - SkRegion mClipRegion; - RectangleList mRectangleList; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif /* CLIPAREA_H_ */ diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h deleted file mode 100644 index b424f97a5004..000000000000 --- a/libs/hwui/FloatColor.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef FLOATCOLOR_H -#define FLOATCOLOR_H - -#include "utils/Color.h" -#include "utils/Macros.h" -#include "utils/MathUtils.h" - -#include <stdint.h> - -namespace android { -namespace uirenderer { - -struct FloatColor { - // "color" is a gamma-encoded sRGB color - // After calling this method, the color is stored as a pre-multiplied linear color - // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied - // gamma-encoded sRGB color - void set(uint32_t color) { - a = ((color >> 24) & 0xff) / 255.0f; - r = a * EOCF(((color >> 16) & 0xff) / 255.0f); - g = a * EOCF(((color >> 8) & 0xff) / 255.0f); - b = a * EOCF(((color)&0xff) / 255.0f); - } - - // "color" is a gamma-encoded sRGB color - // After calling this method, the color is stored as a un-premultiplied linear color - // if linear blending is enabled. Otherwise, the color is stored as a un-premultiplied - // gamma-encoded sRGB color - void setUnPreMultiplied(uint32_t color) { - a = ((color >> 24) & 0xff) / 255.0f; - r = EOCF(((color >> 16) & 0xff) / 255.0f); - g = EOCF(((color >> 8) & 0xff) / 255.0f); - b = EOCF(((color)&0xff) / 255.0f); - } - - bool isNotBlack() { return a < 1.0f || r > 0.0f || g > 0.0f || b > 0.0f; } - - bool operator==(const FloatColor& other) const { - return MathUtils::areEqual(r, other.r) && MathUtils::areEqual(g, other.g) && - MathUtils::areEqual(b, other.b) && MathUtils::areEqual(a, other.a); - } - - bool operator!=(const FloatColor& other) const { return !(*this == other); } - - float r; - float g; - float b; - float a; -}; - -REQUIRE_COMPATIBLE_LAYOUT(FloatColor); - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif /* FLOATCOLOR_H */ diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp deleted file mode 100644 index 65bee476f14d..000000000000 --- a/libs/hwui/ResourceCache.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ResourceCache.h" - -namespace android { - -using namespace uirenderer; -ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache); - -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Resource cache -/////////////////////////////////////////////////////////////////////////////// - -void ResourceCache::logCache() { - ALOGD("ResourceCache: cacheReport:"); - for (size_t i = 0; i < mCache->size(); ++i) { - ResourceReference* ref = mCache->valueAt(i); - ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i), - mCache->valueAt(i)); - ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", i, - ref->refCount, ref->destroyed, ref->resourceType); - } -} - -ResourceCache::ResourceCache() { - Mutex::Autolock _l(mLock); - mCache = new KeyedVector<const void*, ResourceReference*>(); -} - -ResourceCache::~ResourceCache() { - Mutex::Autolock _l(mLock); - delete mCache; -} - -void ResourceCache::lock() { - mLock.lock(); -} - -void ResourceCache::unlock() { - mLock.unlock(); -} - -void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) { - Mutex::Autolock _l(mLock); - incrementRefcountLocked(resource, resourceType); -} - -void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { - incrementRefcount((void*)patchResource, kNinePatch); -} - -void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; - if (ref == nullptr || mCache->size() == 0) { - ref = new ResourceReference(resourceType); - mCache->add(resource, ref); - } - ref->refCount++; -} - -void ResourceCache::decrementRefcount(void* resource) { - Mutex::Autolock _l(mLock); - decrementRefcountLocked(resource); -} - -void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { - decrementRefcount((void*)patchResource); -} - -void ResourceCache::decrementRefcountLocked(void* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; - if (ref == nullptr) { - // Should not get here - shouldn't get a call to decrement if we're not yet tracking it - return; - } - ref->refCount--; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - -void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { - decrementRefcountLocked((void*)patchResource); -} - -void ResourceCache::destructor(Res_png_9patch* resource) { - Mutex::Autolock _l(mLock); - destructorLocked(resource); -} - -void ResourceCache::destructorLocked(Res_png_9patch* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; - if (ref == nullptr) { - // If we're not tracking this resource, just delete it - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; - return; - } - ref->destroyed = true; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - -/** - * This method should only be called while the mLock mutex is held (that mutex is grabbed - * by the various destructor() and recycle() methods which call this method). - */ -void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { - if (ref->destroyed) { - switch (ref->resourceType) { - case kNinePatch: { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; - } break; - } - } - mCache->removeItem(resource); - delete ref; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h deleted file mode 100644 index fd3f9fd05d58..000000000000 --- a/libs/hwui/ResourceCache.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_RESOURCE_CACHE_H -#define ANDROID_HWUI_RESOURCE_CACHE_H - -#include <cutils/compiler.h> - -#include <SkBitmap.h> -#include <SkPixelRef.h> - -#include <utils/KeyedVector.h> -#include <utils/Singleton.h> - -#include <androidfw/ResourceTypes.h> - -namespace android { -namespace uirenderer { - -class Layer; - -/** - * Type of Resource being cached - */ -enum ResourceType { - kNinePatch, -}; - -class ResourceReference { -public: - explicit ResourceReference(ResourceType type) { - refCount = 0; - destroyed = false; - resourceType = type; - } - - int refCount; - bool destroyed; - ResourceType resourceType; -}; - -class ANDROID_API ResourceCache : public Singleton<ResourceCache> { - ResourceCache(); - ~ResourceCache(); - - friend class Singleton<ResourceCache>; - -public: - /** - * When using these two methods, make sure to only invoke the *Locked() - * variants of increment/decrementRefcount(), recyle() and destructor() - */ - void lock(); - void unlock(); - - void incrementRefcount(const Res_png_9patch* resource); - - void decrementRefcount(const Res_png_9patch* resource); - - void decrementRefcountLocked(const Res_png_9patch* resource); - - void destructor(Res_png_9patch* resource); - - void destructorLocked(Res_png_9patch* resource); - -private: - void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref); - - void incrementRefcount(void* resource, ResourceType resourceType); - void incrementRefcountLocked(void* resource, ResourceType resourceType); - - void decrementRefcount(void* resource); - void decrementRefcountLocked(void* resource); - - void logCache(); - - /** - * Used to increment, decrement, and destroy. Incrementing is generally accessed on the UI - * thread, but destroying resources may be called from the GC thread, the finalizer thread, - * or a reference queue finalization thread. - */ - mutable Mutex mLock; - - KeyedVector<const void*, ResourceReference*>* mCache; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_RESOURCE_CACHE_H diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp deleted file mode 100644 index f1a1bef7c94e..000000000000 --- a/libs/hwui/Snapshot.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Snapshot.h" - -#include "hwui/Canvas.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Constructors -/////////////////////////////////////////////////////////////////////////////// - -Snapshot::Snapshot() - : flags(0) - , previous(nullptr) - , layer(nullptr) - , fbo(0) - , alpha(1.0f) - , roundRectClipState(nullptr) - , projectionPathMask(nullptr) - , mClipArea(&mClipAreaRoot) { - transform = &mTransformRoot; - mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0; -} - -/** - * Copies the specified snapshot/ The specified snapshot is stored as - * the previous snapshot. - */ -Snapshot::Snapshot(Snapshot* s, int saveFlags) - : flags(0) - , previous(s) - , layer(s->layer) - , fbo(s->fbo) - , alpha(s->alpha) - , roundRectClipState(s->roundRectClipState) - , projectionPathMask(s->projectionPathMask) - , mClipArea(nullptr) - , mViewportData(s->mViewportData) - , mRelativeLightCenter(s->mRelativeLightCenter) { - if (saveFlags & SaveFlags::Matrix) { - mTransformRoot = *s->transform; - transform = &mTransformRoot; - } else { - transform = s->transform; - } - - if (saveFlags & SaveFlags::Clip) { - mClipAreaRoot = s->getClipArea(); - mClipArea = &mClipAreaRoot; - } else { - mClipArea = s->mClipArea; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Clipping -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::clip(const Rect& localClip, SkClipOp op) { - flags |= Snapshot::kFlagClipSet; - mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op)); -} - -void Snapshot::clipPath(const SkPath& path, SkClipOp op) { - flags |= Snapshot::kFlagClipSet; - mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op)); -} - -void Snapshot::setClip(float left, float top, float right, float bottom) { - flags |= Snapshot::kFlagClipSet; - mClipArea->setClip(left, top, right, bottom); -} - -bool Snapshot::hasPerspectiveTransform() const { - return transform->isPerspective(); -} - -const Rect& Snapshot::getLocalClip() { - mat4 inverse; - inverse.loadInverse(*transform); - - mLocalClip.set(mClipArea->getClipRect()); - inverse.mapRect(mLocalClip); - - return mLocalClip; -} - -void Snapshot::resetClip(float left, float top, float right, float bottom) { - // TODO: This is incorrect, when we start rendering into a new layer, - // we may have to modify the previous snapshot's clip rect and clip - // region if the previous restore() call did not restore the clip - mClipArea = &mClipAreaRoot; - setClip(left, top, right, bottom); -} - -/////////////////////////////////////////////////////////////////////////////// -// Clipping round rect -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, - bool highPriority) { - if (bounds.isEmpty()) { - mClipArea->setEmpty(); - return; - } - - if (roundRectClipState && roundRectClipState->highPriority) { - // ignore, don't replace, already have a high priority clip - return; - } - - RoundRectClipState* state = new (allocator) RoundRectClipState; - - state->highPriority = highPriority; - - // store the inverse drawing matrix - Matrix4 roundRectDrawingMatrix = getOrthoMatrix(); - roundRectDrawingMatrix.multiply(*transform); - state->matrix.loadInverse(roundRectDrawingMatrix); - - // compute area under rounded corners - only draws overlapping these rects need to be clipped - for (int i = 0; i < 4; i++) { - state->dangerRects[i] = bounds; - } - state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; - state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius; - state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius; - state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius; - for (int i = 0; i < 4; i++) { - transform->mapRect(state->dangerRects[i]); - - // round danger rects out as though they are AA geometry (since they essentially are) - state->dangerRects[i].snapGeometryToPixelBoundaries(true); - } - - // store RR area - state->innerRect = bounds; - state->innerRect.inset(radius); - state->radius = radius; - - // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info - roundRectClipState = state; -} - -void Snapshot::setProjectionPathMask(const SkPath* path) { - projectionPathMask = path; -} - -static Snapshot* getClipRoot(Snapshot* target) { - while (target->previous && target->previous->previous) { - target = target->previous; - } - return target; -} - -const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, - const Matrix4& recordedClipTransform) { - auto target = this; - if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { - // Clip must be intersected with root, instead of current clip. - target = getClipRoot(this); - } - - return target->mClipArea->serializeIntersectedClip(allocator, recordedClip, - recordedClipTransform); -} - -void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) { - if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { - // current clip is being replaced, but must intersect with clip root - *mClipArea = *(getClipRoot(this)->mClipArea); - } - mClipArea->applyClip(recordedClip, transform); -} - -/////////////////////////////////////////////////////////////////////////////// -// Queries -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::dump() const { - ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous, - getViewportHeight(), !mClipArea->isSimple()); - const Rect& clipRect(mClipArea->getClipRect()); - ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top, - clipRect.right, clipRect.bottom, mClipArea->isSimple()); - - ALOGD(" Transform (at %p):", transform); - transform->dump(); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h deleted file mode 100644 index 655f819ca41b..000000000000 --- a/libs/hwui/Snapshot.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -#include <ui/Region.h> -#include <utils/LinearAllocator.h> -#include <utils/RefBase.h> - -#include <SkClipOp.h> -#include <SkRegion.h> - -#include "ClipArea.h" -#include "Layer.h" -#include "Matrix.h" -#include "Outline.h" -#include "Rect.h" -#include "utils/Macros.h" - -namespace android { -namespace uirenderer { - -/** - * Temporary structure holding information for a single outline clip. - * - * These structures are treated as immutable once created, and only exist for a single frame, which - * is why they may only be allocated with a LinearAllocator. - */ -class RoundRectClipState { -public: - static void* operator new(size_t size) = delete; - static void* operator new(size_t size, LinearAllocator& allocator) { - return allocator.alloc<RoundRectClipState>(size); - } - - bool areaRequiresRoundRectClip(const Rect& rect) const { - return rect.intersects(dangerRects[0]) || rect.intersects(dangerRects[1]) || - rect.intersects(dangerRects[2]) || rect.intersects(dangerRects[3]); - } - - bool highPriority; - Matrix4 matrix; - Rect dangerRects[4]; - Rect innerRect; - float radius; -}; - -/** - * A snapshot holds information about the current state of the rendering - * surface. A snapshot is usually created whenever the user calls save() - * and discarded when the user calls restore(). Once a snapshot is created, - * it can hold information for deferred rendering. - * - * Each snapshot has a link to a previous snapshot, indicating the previous - * state of the renderer. - */ -class Snapshot { -public: - Snapshot(); - Snapshot(Snapshot* s, int saveFlags); - - /** - * Various flags set on ::flags. - */ - enum Flags { - /** - * Indicates that the clip region was modified. When this - * snapshot is restored so must the clip. - */ - kFlagClipSet = 0x1, - /** - * Indicates that this snapshot was created when saving - * a new layer. - */ - kFlagIsLayer = 0x2, - /** - * Indicates that this snapshot is a special type of layer - * backed by an FBO. This flag only makes sense when the - * flag kFlagIsLayer is also set. - * - * Viewport has been modified to fit the new Fbo, and must be - * restored when this snapshot is restored. - */ - kFlagIsFboLayer = 0x4, - }; - - /** - * Modifies the current clip with the new clip rectangle and - * the specified operation. The specified rectangle is transformed - * by this snapshot's trasnformation. - */ - void clip(const Rect& localClip, SkClipOp op); - - /** - * Modifies the current clip with the new clip rectangle and - * the specified operation. The specified rectangle is considered - * already transformed. - */ - void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect); - - /** - * Modifies the current clip with the specified path and operation. - */ - void clipPath(const SkPath& path, SkClipOp op); - - /** - * Sets the current clip. - */ - void setClip(float left, float top, float right, float bottom); - - /** - * Returns the current clip in local coordinates. The clip rect is - * transformed by the inverse transform matrix. - */ - ANDROID_API const Rect& getLocalClip(); - - /** - * Returns the current clip in render target coordinates. - */ - const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); } - - /* - * Accessor functions so that the clip area can stay private - */ - bool clipIsEmpty() const { return mClipArea->isEmpty(); } - const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); } - bool clipIsSimple() const { return mClipArea->isSimple(); } - const ClipArea& getClipArea() const { return *mClipArea; } - ClipArea& mutateClipArea() { return *mClipArea; } - - WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip( - LinearAllocator& allocator, const ClipBase* recordedClip, - const Matrix4& recordedClipTransform); - void applyClip(const ClipBase* clip, const Matrix4& transform); - - /** - * Resets the clip to the specified rect. - */ - void resetClip(float left, float top, float right, float bottom); - - void initializeViewport(int width, int height) { - mViewportData.initialize(width, height); - mClipAreaRoot.setViewportDimensions(width, height); - } - - int getViewportWidth() const { return mViewportData.mWidth; } - int getViewportHeight() const { return mViewportData.mHeight; } - const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; } - - const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; } - void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; } - - /** - * Sets (and replaces) the current clipping outline - * - * If the current round rect clip is high priority, the incoming clip is ignored. - */ - void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, - bool highPriority); - - /** - * Sets (and replaces) the current projection mask - */ - void setProjectionPathMask(const SkPath* path); - - /** - * Indicates whether the current transform has perspective components. - */ - bool hasPerspectiveTransform() const; - - /** - * Dirty flags. - */ - int flags; - - /** - * Previous snapshot. - */ - Snapshot* previous; - - /** - * A pointer to the currently active layer. - * - * This snapshot does not own the layer, this pointer must not be freed. - */ - Layer* layer; - - /** - * Target FBO used for rendering. Set to 0 when rendering directly - * into the framebuffer. - */ - GLuint fbo; - - /** - * Local transformation. Holds the current translation, scale and - * rotation values. - * - * This is a reference to a matrix owned by this snapshot or another - * snapshot. This pointer must not be freed. See ::mTransformRoot. - */ - mat4* transform; - - /** - * Current alpha value. This value is 1 by default, but may be set by a DisplayList which - * has translucent rendering in a non-overlapping View. This value will be used by - * the renderer to set the alpha in the current color being used for ensuing drawing - * operations. The value is inherited by child snapshots because the same value should - * be applied to descendants of the current DisplayList (for example, a TextView contains - * the base alpha value which should be applied to the child DisplayLists used for drawing - * the actual text). - */ - float alpha; - - /** - * Current clipping round rect. - * - * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips, - * never modified. - */ - const RoundRectClipState* roundRectClipState; - - /** - * Current projection masking path - used exclusively to mask projected, tessellated circles. - */ - const SkPath* projectionPathMask; - - void dump() const; - -private: - struct ViewportData { - ViewportData() : mWidth(0), mHeight(0) {} - void initialize(int width, int height) { - mWidth = width; - mHeight = height; - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); - } - - /* - * Width and height of current viewport. - * - * The viewport is always defined to be (0, 0, width, height). - */ - int mWidth; - int mHeight; - /** - * Contains the current orthographic, projection matrix. - */ - mat4 mOrthoMatrix; - }; - - mat4 mTransformRoot; - - ClipArea mClipAreaRoot; - ClipArea* mClipArea; - Rect mLocalClip; - - ViewportData mViewportData; - Vector3 mRelativeLightCenter; - -}; // class Snapshot - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index a6aae55b2ee8..f0912777e3d8 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -19,7 +19,6 @@ #include "Vector.h" -#include "FloatColor.h" #include "utils/Macros.h" namespace android { diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index b04194f378bc..753557c2e120 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -224,6 +224,7 @@ Bitmap::~Bitmap() { break; case PixelStorageType::Heap: free(mPixelStorage.heap.address); + mallopt(M_PURGE, 0); break; case PixelStorageType::Hardware: auto buffer = mPixelStorage.hardware.buffer; diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index a00b8db3c617..c5db861d4f48 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -21,7 +21,6 @@ #include <Properties.h> #include <Rect.h> #include <RenderNode.h> -#include <Snapshot.h> #include <hwui/Bitmap.h> #include <pipeline/skia/SkiaRecordingCanvas.h> #include <private/hwui/DrawGlInfo.h> @@ -141,14 +140,6 @@ public: return true; } - static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) { - std::unique_ptr<Snapshot> snapshot(new Snapshot()); - // store clip first, so it isn't transformed - snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom); - *(snapshot->transform) = transform; - return snapshot; - } - static sk_sp<Bitmap> createBitmap(int width, int height, SkColorType colorType = kN32_SkColorType) { SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType); diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 9388c2062736..70423a70157b 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -16,7 +16,6 @@ #include <benchmark/benchmark.h> -#include "CanvasState.h" #include "DisplayList.h" #include "hwui/Canvas.h" #include "pipeline/skia/SkiaDisplayList.h" @@ -116,52 +115,6 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) } BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView); -class NullClient : public CanvasStateClient { - void onViewportInitialized() override {} - void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} - GLuint getTargetFbo() const override { return 0; } -}; - -void BM_CanvasState_saverestore(benchmark::State& benchState) { - NullClient client; - CanvasState state(client); - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - - while (benchState.KeepRunning()) { - state.save(SaveFlags::MatrixClip); - state.save(SaveFlags::MatrixClip); - benchmark::DoNotOptimize(&state); - state.restore(); - state.restore(); - } -} -BENCHMARK(BM_CanvasState_saverestore); - -void BM_CanvasState_init(benchmark::State& benchState) { - NullClient client; - CanvasState state(client); - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - - while (benchState.KeepRunning()) { - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - benchmark::DoNotOptimize(&state); - } -} -BENCHMARK(BM_CanvasState_init); - -void BM_CanvasState_translate(benchmark::State& benchState) { - NullClient client; - CanvasState state(client); - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - - while (benchState.KeepRunning()) { - state.translate(5, 5, 0); - benchmark::DoNotOptimize(&state); - state.translate(-5, -5, 0); - } -} -BENCHMARK(BM_CanvasState_translate); - void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp deleted file mode 100644 index 4c03811b0c96..000000000000 --- a/libs/hwui/tests/unit/CanvasStateTests.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CanvasState.h" - -#include "Matrix.h" -#include "Rect.h" -#include "hwui/Canvas.h" -#include "utils/LinearAllocator.h" - -#include <SkClipOp.h> -#include <SkPath.h> -#include <gtest/gtest.h> - -namespace android { -namespace uirenderer { - -class NullClient : public CanvasStateClient { - void onViewportInitialized() override {} - void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} - GLuint getTargetFbo() const override { return 0; } -}; - -static NullClient sNullClient; - -static bool approxEqual(const Matrix4& a, const Matrix4& b) { - for (int i = 0; i < 16; i++) { - if (!MathUtils::areEqual(a[i], b[i])) { - return false; - } - } - return true; -} - -TEST(CanvasState, gettersAndSetters) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - ASSERT_EQ(state.getWidth(), 200); - ASSERT_EQ(state.getHeight(), 200); - - Matrix4 simpleTranslate; - simpleTranslate.loadTranslate(10, 20, 0); - state.setMatrix(simpleTranslate); - - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); - ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180)); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); - EXPECT_TRUE(state.clipIsSimple()); -} - -TEST(CanvasState, simpleClipping) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100)); - - state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100)); - - state.clipRect(50, 50, 150, 150, SkClipOp::kReplace_deprecated); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150)); -} - -TEST(CanvasState, complexClipping) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.save(SaveFlags::MatrixClip); - { - // rotated clip causes complex clip - state.rotate(10); - EXPECT_TRUE(state.clipIsSimple()); - state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); - EXPECT_FALSE(state.clipIsSimple()); - } - state.restore(); - - state.save(SaveFlags::MatrixClip); - { - // subtracted clip causes complex clip - EXPECT_TRUE(state.clipIsSimple()); - state.clipRect(50, 50, 150, 150, SkClipOp::kDifference); - EXPECT_FALSE(state.clipIsSimple()); - } - state.restore(); - - state.save(SaveFlags::MatrixClip); - { - // complex path causes complex clip - SkPath path; - path.addOval(SkRect::MakeWH(200, 200)); - EXPECT_TRUE(state.clipIsSimple()); - state.clipPath(&path, SkClipOp::kDifference); - EXPECT_FALSE(state.clipIsSimple()); - } - state.restore(); -} - -TEST(CanvasState, saveAndRestore) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.save(SaveFlags::Clip); - { - state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); - } - state.restore(); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore - - Matrix4 simpleTranslate; - simpleTranslate.loadTranslate(10, 10, 0); - state.save(SaveFlags::Matrix); - { - state.translate(10, 10, 0); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); - } - state.restore(); - EXPECT_FALSE(approxEqual(*state.currentTransform(), simpleTranslate)); -} - -TEST(CanvasState, saveAndRestoreButNotTooMuch) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.save(SaveFlags::Matrix); // NOTE: clip not saved - { - state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); - } - state.restore(); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored - - Matrix4 simpleTranslate; - simpleTranslate.loadTranslate(10, 10, 0); - state.save(SaveFlags::Clip); // NOTE: matrix not saved - { - state.translate(10, 10, 0); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); - } - state.restore(); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored -} -} -} diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp deleted file mode 100644 index 450bb679a45f..000000000000 --- a/libs/hwui/tests/unit/ClipAreaTests.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <SkPath.h> -#include <SkRegion.h> -#include <gtest/gtest.h> - -#include "ClipArea.h" - -#include "Matrix.h" -#include "Rect.h" -#include "utils/LinearAllocator.h" - -namespace android { -namespace uirenderer { - -static Rect kViewportBounds(2048, 2048); - -static ClipArea createClipArea() { - ClipArea area; - area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight()); - return area; -} - -TEST(TransformedRectangle, basics) { - Rect r(0, 0, 100, 100); - Matrix4 minus90; - minus90.loadRotate(-90); - minus90.mapRect(r); - Rect r2(20, 40, 120, 60); - - Matrix4 m90; - m90.loadRotate(90); - TransformedRectangle tr(r, m90); - EXPECT_TRUE(tr.canSimplyIntersectWith(tr)); - - Matrix4 m0; - TransformedRectangle tr0(r2, m0); - EXPECT_FALSE(tr.canSimplyIntersectWith(tr0)); - - Matrix4 m45; - m45.loadRotate(45); - TransformedRectangle tr2(r, m45); - EXPECT_FALSE(tr2.canSimplyIntersectWith(tr)); -} - -TEST(RectangleList, basics) { - RectangleList list; - EXPECT_TRUE(list.isEmpty()); - - Rect r(0, 0, 100, 100); - Matrix4 m45; - m45.loadRotate(45); - list.set(r, m45); - EXPECT_FALSE(list.isEmpty()); - - Rect r2(20, 20, 200, 200); - list.intersectWith(r2, m45); - EXPECT_FALSE(list.isEmpty()); - EXPECT_EQ(1, list.getTransformedRectanglesCount()); - - Rect r3(20, 20, 200, 200); - Matrix4 m30; - m30.loadRotate(30); - list.intersectWith(r2, m30); - EXPECT_FALSE(list.isEmpty()); - EXPECT_EQ(2, list.getTransformedRectanglesCount()); - - SkRegion clip; - clip.setRect(0, 0, 2000, 2000); - SkRegion rgn(list.convertToRegion(clip)); - EXPECT_FALSE(rgn.isEmpty()); -} - -TEST(ClipArea, basics) { - ClipArea area(createClipArea()); - EXPECT_FALSE(area.isEmpty()); -} - -TEST(ClipArea, paths) { - ClipArea area(createClipArea()); - SkPath path; - SkScalar r = 100; - path.addCircle(r, r, r); - area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op); - EXPECT_FALSE(area.isEmpty()); - EXPECT_FALSE(area.isSimple()); - EXPECT_FALSE(area.isRectangleList()); - - Rect clipRect(area.getClipRect()); - Rect expected(0, 0, r * 2, r * 2); - EXPECT_EQ(expected, clipRect); - SkRegion clipRegion(area.getClipRegion()); - auto skRect(clipRegion.getBounds()); - Rect regionBounds; - regionBounds.set(skRect); - EXPECT_EQ(expected, regionBounds); -} - -TEST(ClipArea, replaceNegative) { - ClipArea area(createClipArea()); - area.setClip(0, 0, 100, 100); - - Rect expected(-50, -50, 50, 50); - area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op); - EXPECT_EQ(expected, area.getClipRect()); -} - -TEST(ClipArea, serializeClip) { - ClipArea area(createClipArea()); - LinearAllocator allocator; - - // unset clip - EXPECT_EQ(nullptr, area.serializeClip(allocator)); - - // rect clip - area.setClip(0, 0, 200, 200); - { - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode); - ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; - EXPECT_EQ(Rect(200, 200), serializedClip->rect); - EXPECT_EQ(serializedClip, area.serializeClip(allocator)) - << "Requery of clip on unmodified ClipArea must return same pointer."; - } - - // rect list - Matrix4 rotate; - rotate.loadRotate(5.0f); - area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op); - { - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode); - ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; - auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip); - EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); - EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect); - EXPECT_EQ(serializedClip, area.serializeClip(allocator)) - << "Requery of clip on unmodified ClipArea must return same pointer."; - } - - // region - SkPath circlePath; - circlePath.addCircle(100, 100, 100); - area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); - { - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - ASSERT_EQ(ClipMode::Region, serializedClip->mode); - ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot"; - auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip); - EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds()) - << "Clip region should be 200x200"; - EXPECT_EQ(Rect(200, 200), clipRegion->rect); - EXPECT_EQ(serializedClip, area.serializeClip(allocator)) - << "Requery of clip on unmodified ClipArea must return same pointer."; - } -} - -TEST(ClipArea, serializeClip_pathIntersectWithRoot) { - ClipArea area(createClipArea()); - LinearAllocator allocator; - SkPath circlePath; - circlePath.addCircle(100, 100, 100); - area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op); - - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; -} - -TEST(ClipArea, serializeIntersectedClip) { - ClipArea area(createClipArea()); - LinearAllocator allocator; - - // simple state; - EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); - area.setClip(0, 0, 200, 200); - { - auto origRectClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, origRectClip); - EXPECT_EQ(origRectClip, - area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); - } - - // rect - { - ClipRect recordedClip(Rect(100, 100)); - Matrix4 translateScale; - translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 3, 1); - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); - ASSERT_NE(nullptr, resolvedClip); - ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect); - - EXPECT_EQ(resolvedClip, - area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) - << "Must return previous serialization, since input is same"; - - ClipRect recordedClip2(Rect(100, 100)); - EXPECT_NE(resolvedClip, - area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) - << "Shouldn't return previous serialization, since matrix location is different"; - } - - // rect list - Matrix4 rotate; - rotate.loadRotate(2.0f); - area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); - { - ClipRect recordedClip(Rect(100, 100)); - auto resolvedClip = - area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); - ASSERT_NE(nullptr, resolvedClip); - ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode); - auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip); - EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); - } - - // region - SkPath circlePath; - circlePath.addCircle(100, 100, 100); - area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); - { - SkPath ovalPath; - ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200)); - - ClipRegion recordedClip; - recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200))); - recordedClip.rect = Rect(200, 200); - - Matrix4 translate10x20; - translate10x20.loadTranslate(10, 20, 0); - auto resolvedClip = area.serializeIntersectedClip( - allocator, &recordedClip, - translate10x20); // Note: only translate for now, others not handled correctly - ASSERT_NE(nullptr, resolvedClip); - ASSERT_EQ(ClipMode::Region, resolvedClip->mode); - auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); - EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds()); - } -} - -TEST(ClipArea, serializeIntersectedClip_snap) { - ClipArea area(createClipArea()); - area.setClip(100.2, 100.4, 500.6, 500.8); - LinearAllocator allocator; - - { - // no recorded clip case - auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()); - EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect); - } - { - // recorded clip case - ClipRect recordedClip(Rect(100.12, 100.74)); - Matrix4 translateScale; - translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 3, - 1); // recorded clip will have non-int coords, even after transform - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); - ASSERT_NE(nullptr, resolvedClip); - EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect); - } -} - -TEST(ClipArea, serializeIntersectedClip_scale) { - ClipArea area(createClipArea()); - area.setClip(0, 0, 400, 400); - LinearAllocator allocator; - - SkPath circlePath; - circlePath.addCircle(50, 50, 50); - - ClipRegion recordedClip; - recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100))); - recordedClip.rect = Rect(100, 100); - - Matrix4 translateScale; - translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 2, 1); - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); - - ASSERT_NE(nullptr, resolvedClip); - EXPECT_EQ(ClipMode::Region, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect); - auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); - EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_identity) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - ClipArea::applyTransformToRegion(Matrix4::identity(), ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_translate) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.loadTranslate(10, 20, 0); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_scale) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.loadScale(2, 3, 1); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_translateScale) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.translate(10, 20); - transform.scale(2, 3, 1); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_rotate90) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.loadRotate(90); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds()); -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 2926ef3a2d95..2c73940b9b9c 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -32,6 +32,7 @@ #include "pipeline/skia/SkiaRecordingCanvas.h" #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" +#include "utils/Color.h" using namespace android; using namespace android::uirenderer; diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp deleted file mode 100644 index 9d673c82d4d0..000000000000 --- a/libs/hwui/tests/unit/SnapshotTests.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gtest/gtest.h> - -#include <Snapshot.h> - -#include <tests/common/TestUtils.h> - -using namespace android::uirenderer; - -TEST(Snapshot, serializeIntersectedClip) { - auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100)); - auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90)); - auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); - root->previous = actualRoot.get(); - child->previous = root.get(); - - LinearAllocator allocator; - ClipRect rect(Rect(0, 0, 75, 75)); - { - auto intersectWithChild = - child->serializeIntersectedClip(allocator, &rect, Matrix4::identity()); - ASSERT_NE(nullptr, intersectWithChild); - EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child"; - } - - rect.intersectWithRoot = true; - { - auto intersectWithRoot = - child->serializeIntersectedClip(allocator, &rect, Matrix4::identity()); - ASSERT_NE(nullptr, intersectWithRoot); - EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root"; - } -} - -TEST(Snapshot, applyClip) { - auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100)); - auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90)); - root->previous = actualRoot.get(); - - ClipRect rect(Rect(0, 0, 75, 75)); - { - auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); - child->previous = root.get(); - child->applyClip(&rect, Matrix4::identity()); - - EXPECT_TRUE(child->getClipArea().isSimple()); - EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip()); - } - - { - rect.intersectWithRoot = true; - auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); - child->previous = root.get(); - child->applyClip(&rect, Matrix4::identity()); - - EXPECT_TRUE(child->getClipArea().isSimple()); - EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip()); - } -} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c074ccebc62c..e8c97bc0161f 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3942,18 +3942,31 @@ public class AudioManager { } /** - * Indicate Hearing Aid connection state change. + * Indicate Hearing Aid connection state change and eventually suppress + * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. * @param device Bluetooth device connected/disconnected * @param state new connection state (BluetoothProfile.STATE_xxx) + * @param musicDevice Default get system volume for the connecting device. + * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or + * {@link android.bluetooth.BluetoothProfile.HEARING_AID}) + * @param suppressNoisyIntent if true the + * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. + * @return a delay in ms that the caller should wait before broadcasting + * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent. * {@hide} */ - public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) { + public int setBluetoothHearingAidDeviceConnectionState( + BluetoothDevice device, int state, boolean suppressNoisyIntent, + int musicDevice) { final IAudioService service = getService(); + int delay = 0; try { - service.setHearingAidDeviceConnectionState(device, state); + delay = service.setBluetoothHearingAidDeviceConnectionState(device, + state, suppressNoisyIntent, musicDevice); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } + return delay; } /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 569db16c312e..abd64119de61 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -151,8 +151,6 @@ interface IAudioService { void setWiredDeviceConnectionState(int type, int state, String address, String name, String caller); - void setHearingAidDeviceConnectionState(in BluetoothDevice device, int state); - int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile); void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device); @@ -210,6 +208,9 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); + int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device, + int state, boolean suppressNoisyIntent, int musicDevice); + int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 6d122d77a999..ee12b913765a 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -1192,15 +1192,17 @@ public final class MediaDrm implements AutoCloseable { public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5; /** - * The maximum security level supported by the device. This is the default - * security level when a session is opened. + * Indicates that the maximum security level supported by the device should + * be used when opening a session. This is the default security level + * selected when a session is opened. * @hide */ public static final int SECURITY_LEVEL_MAX = 6; /** - * The maximum security level supported by the device. This is the default - * security level when a session is opened. + * Returns a value that may be passed as a parameter to {@link #openSession(int)} + * requesting that the session be opened at the maximum security level of + * the device. */ public static final int getMaxSecurityLevel() { return SECURITY_LEVEL_MAX; diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index a0da33014b03..71df7dce5ea2 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -738,59 +738,67 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies, long startPos, long endPos) throws IOException { - // The context and URI usually belong to the calling user. Get a resolver for that user - // and strip out the userId from the URI if present. + // The context and URI usually belong to the calling user. Get a resolver for that user. final ContentResolver resolver = context.getContentResolver(); final String scheme = uri.getScheme(); - final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()); if (ContentResolver.SCHEME_FILE.equals(scheme)) { handleDataSource(isCurrent, srcId, uri.getPath(), null, null, startPos, endPos); return; } - AssetFileDescriptor afd = null; + final int ringToneType = RingtoneManager.getDefaultType(uri); try { + AssetFileDescriptor afd; // Try requested Uri locally first - if (ContentResolver.SCHEME_CONTENT.equals(scheme) - && Settings.AUTHORITY.equals(authority)) { + if (ContentResolver.SCHEME_CONTENT.equals(scheme) && ringToneType != -1) { afd = RingtoneManager.openDefaultRingtoneUri(context, uri); + if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) { + return; + } + final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri( + context, ringToneType); + afd = resolver.openAssetFileDescriptor(actualUri, "r"); } else { afd = resolver.openAssetFileDescriptor(uri, "r"); } - if (afd != null) { - handleDataSource(isCurrent, srcId, afd, startPos, endPos); + if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) { return; } } catch (NullPointerException | SecurityException | IOException ex) { Log.w(TAG, "Couldn't open " + uri + ": " + ex); // Fallback to media server - } finally { - if (afd != null) { - afd.close(); - } } handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies, startPos, endPos); } - private void handleDataSource(boolean isCurrent, long srcId, AssetFileDescriptor afd, - long startPos, long endPos) - throws IOException { - if (afd.getDeclaredLength() < 0) { - handleDataSource(isCurrent, - srcId, - afd.getFileDescriptor(), - 0, - DataSourceDesc.LONG_MAX, - startPos, - endPos); - } else { - handleDataSource(isCurrent, - srcId, - afd.getFileDescriptor(), - afd.getStartOffset(), - afd.getDeclaredLength(), - startPos, - endPos); + private boolean attemptDataSource(boolean isCurrent, long srcId, AssetFileDescriptor afd, + long startPos, long endPos) throws IOException { + try { + if (afd.getDeclaredLength() < 0) { + handleDataSource(isCurrent, + srcId, + afd.getFileDescriptor(), + 0, + DataSourceDesc.LONG_MAX, + startPos, + endPos); + } else { + handleDataSource(isCurrent, + srcId, + afd.getFileDescriptor(), + afd.getStartOffset(), + afd.getDeclaredLength(), + startPos, + endPos); + } + return true; + } catch (NullPointerException | SecurityException | IOException ex) { + Log.w(TAG, "Couldn't open srcId:" + srcId + ": " + ex); + return false; + } finally { + if (afd != null) { + afd.close(); + } } } 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/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/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index d60dbe783fcd..5161344a4946 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -2,16 +2,14 @@ android_library { name: "SettingsLib", - libs: [ + static_libs: [ "androidx.annotation_annotation", "androidx.legacy_legacy-support-v4", "androidx.recyclerview_recyclerview", "androidx.preference_preference", "androidx.appcompat_appcompat", "androidx.lifecycle_lifecycle-runtime", - ], - static_libs: [ "SettingsLibHelpUtils", "SettingsLibRestrictedLockUtils", "SettingsLibAppPreference", diff --git a/packages/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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3bdd60130de3..cc9adb86a6b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -5277,8 +5277,7 @@ public class StatusBar extends SystemUI implements DemoMode, (Runnable saveImportance, StatusBarNotification sbn) -> { // If the user has security enabled, show challenge if the setting is changed. if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier()) - && (mState == StatusBarState.KEYGUARD || - mState == StatusBarState.SHADE_LOCKED)) { + && mKeyguardManager.isKeyguardLocked()) { onLockedNotificationImportanceChange(() -> { saveImportance.run(); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 45b32c7abae0..ad9b9b30fafc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -58,6 +58,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.FloatingToolbar; import com.android.systemui.Dependency; +import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.DragDownHelper; @@ -182,6 +183,11 @@ public class StatusBarWindowView extends FrameLayout { } } + @VisibleForTesting + protected NotificationStackScrollLayout getStackScrollLayout() { + return mStackScrollLayout; + } + @Override public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); @@ -215,8 +221,11 @@ public class StatusBarWindowView extends FrameLayout { public void setService(StatusBar service) { mService = service; - setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout, - mStackScrollLayout)); + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback(); + DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback(); + setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback, + dragDownCallback)); } @VisibleForTesting @@ -309,7 +318,7 @@ public class StatusBarWindowView extends FrameLayout { } } if (isDown) { - mStackScrollLayout.closeControlsIfOutsideTouch(ev); + getStackScrollLayout().closeControlsIfOutsideTouch(ev); } if (mService.isDozing()) { mService.mDozeScrimController.extendPulse(); @@ -331,13 +340,14 @@ public class StatusBarWindowView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mService.isDozing() && !mStackScrollLayout.hasPulsingNotifications()) { + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) { // Capture all touch events in always-on. return true; } boolean intercept = false; if (mNotificationPanel.isFullyExpanded() - && mStackScrollLayout.getVisibility() == View.VISIBLE + && stackScrollLayout.getVisibility() == View.VISIBLE && mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mService.isBouncerShowing() && !mService.isDozing()) { @@ -349,7 +359,7 @@ public class StatusBarWindowView extends FrameLayout { if (intercept) { MotionEvent cancellation = MotionEvent.obtain(ev); cancellation.setAction(MotionEvent.ACTION_CANCEL); - mStackScrollLayout.onInterceptTouchEvent(cancellation); + stackScrollLayout.onInterceptTouchEvent(cancellation); mNotificationPanel.onInterceptTouchEvent(cancellation); cancellation.recycle(); } @@ -391,8 +401,9 @@ public class StatusBarWindowView extends FrameLayout { } public void cancelExpandHelper() { - if (mStackScrollLayout != null) { - mStackScrollLayout.cancelExpandHelper(); + NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); + if (stackScrollLayout != null) { + stackScrollLayout.cancelExpandHelper(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 24a28cb45952..70a35892da98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -461,6 +461,8 @@ public class NetworkControllerImpl extends BroadcastReceiver MobileSignalController controller = mMobileSignalControllers.valueAt(i); controller.handleBroadcast(intent); } + mConfig = Config.readConfig(mContext); + mReceiverHandler.post(this::handleConfigurationChanged); break; case TelephonyIntents.ACTION_SIM_STATE_CHANGED: // Avoid rebroadcast because SysUI is direct boot aware. @@ -1042,18 +1044,23 @@ public class NetworkControllerImpl extends BroadcastReceiver config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G); config.alwaysShowCdmaRssi = res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi); - config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE); config.hspaDataDistinguishable = res.getBoolean(R.bool.config_hspa_data_distinguishable); - config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus); config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength); CarrierConfigManager configMgr = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); - PersistableBundle b = configMgr.getConfig(); + // Handle specific carrier config values for the default data SIM + int defaultDataSubId = SubscriptionManager.from(context) + .getDefaultDataSubscriptionId(); + PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId); if (b != null) { config.alwaysShowDataRatIcon = b.getBoolean( CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL); + config.show4gForLte = b.getBoolean( + CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL); + config.hideLtePlus = b.getBoolean( + CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL); } return config; } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a97effd3a023..e20e267336ea 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -956,11 +956,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa changed |= onVolumeChangedW(stream, 0); } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) { final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); + if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm; if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm=" + Util.ringerModeToString(rm)); changed = updateRingerModeExternalW(rm); } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) { final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1); + if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm; if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm=" + Util.ringerModeToString(rm)); changed = updateRingerModeInternalW(rm); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 7abac0054e5d..798f8bcd7938 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -135,10 +135,6 @@ public class VolumeDialogImpl implements VolumeDialog { private final AccessibilityManagerWrapper mAccessibilityMgr; private final Object mSafetyWarningLock = new Object(); private final Accessibility mAccessibility = new Accessibility(); - private ColorStateList mActiveTint; - private int mActiveAlpha; - private ColorStateList mInactiveTint; - private int mInactiveAlpha; private boolean mShowing; private boolean mShowA11yStream; @@ -238,11 +234,6 @@ public class VolumeDialogImpl implements VolumeDialog { lp.gravity = ((FrameLayout.LayoutParams) mDialogView.getLayoutParams()).gravity; mWindow.setAttributes(lp); - mActiveTint = Utils.getColorAccent(mContext); - mActiveAlpha = Color.alpha(mActiveTint.getDefaultColor()); - mInactiveTint = Utils.getColorAttr(mContext, android.R.attr.colorForeground); - mInactiveAlpha = getAlphaAttr(android.R.attr.secondaryContentAlpha); - mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows); mRinger = mDialog.findViewById(R.id.ringer); if (mRinger != null) { @@ -942,8 +933,12 @@ public class VolumeDialogImpl implements VolumeDialog { row.slider.requestFocus(); } boolean useActiveColoring = isActive && row.slider.isEnabled(); - final ColorStateList tint = useActiveColoring ? mActiveTint : mInactiveTint; - final int alpha = useActiveColoring ? mActiveAlpha : mInactiveAlpha; + final ColorStateList tint = useActiveColoring + ? Utils.getColorAccent(mContext) + : Utils.getColorAttr(mContext, android.R.attr.colorForeground); + final int alpha = useActiveColoring + ? Color.alpha(tint.getDefaultColor()) + : getAlphaAttr(android.R.attr.secondaryContentAlpha); if (tint == row.cachedTint) return; row.slider.setProgressTintList(tint); row.slider.setThumbTintList(tint); diff --git a/packages/SystemUI/tests/src/com/android/systemui/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/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index dd368675d312..f85749af54fd 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1813,8 +1813,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return sanitizers; } - // TODO: this method is called a few times in the save process, we should cache its results into - // ViewState. @Nullable private AutofillValue getSanitizedValue( @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers, @@ -1822,13 +1820,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable AutofillValue value) { if (sanitizers == null || value == null) return value; - final InternalSanitizer sanitizer = sanitizers.get(id); - if (sanitizer == null) { - return value; - } + final ViewState state = mViewStates.get(id); + AutofillValue sanitized = state == null ? null : state.getSanitizedValue(); + if (sanitized == null) { + final InternalSanitizer sanitizer = sanitizers.get(id); + if (sanitizer == null) { + return value; + } - final AutofillValue sanitized = sanitizer.sanitize(value); - if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized); + sanitized = sanitizer.sanitize(value); + if (sDebug) { + Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized); + } + if (state != null) { + state.setSanitizedValue(sanitized); + } + } return sanitized; } 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 ca940f5ac735..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; @@ -1044,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 5d2d9024bd9b..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; @@ -99,9 +93,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT; -import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; -import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; -import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; @@ -114,15 +105,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBS import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; -import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; -import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; -import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_NETWORK; @@ -133,11 +121,16 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_O import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE; -import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES; +import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; +import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; +import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK; +import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; +import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; +import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD; import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_SHORT_CMD; import static com.android.server.am.ActivityTaskManagerService.DUMP_CONTAINERS_CMD; @@ -149,6 +142,7 @@ import static com.android.server.am.ActivityTaskManagerService.DUMP_STARTER_CMD; import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS; import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.am.ActivityTaskManagerService.relaunchReasonToString; +import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES; import static com.android.server.am.MemoryStatUtil.hasMemcg; import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; @@ -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 @@ -498,6 +494,9 @@ public class ActivityManagerService extends IActivityManager.Stub */ private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 200; // 0.2 sec + // The minimum memory growth threshold (in KB) for low RAM devices. + private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB + /** * State indicating that there is no need for any blocking for network. */ @@ -618,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[] { @@ -758,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>(); @@ -1095,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. */ @@ -1211,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 { @@ -1423,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; /** @@ -1462,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); @@ -1599,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(); @@ -1613,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: { @@ -1638,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: { @@ -1698,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; } @@ -1832,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; } @@ -1982,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); @@ -1991,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) { @@ -2259,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; @@ -2309,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); @@ -2465,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()); @@ -2720,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) { @@ -3069,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, @@ -3083,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); @@ -3688,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))) { @@ -3983,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 @@ -4064,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, @@ -4089,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--) { @@ -4134,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(); @@ -4155,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; } @@ -4216,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; } @@ -4635,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"); } } @@ -4658,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; @@ -4715,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); @@ -5033,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) { @@ -5228,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)); @@ -5290,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) { @@ -5429,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. @@ -5485,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; } @@ -5529,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; } @@ -5740,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) { @@ -5751,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; } @@ -5924,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) { @@ -6881,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); } // ========================================================= @@ -7329,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. @@ -7371,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(); @@ -7410,8 +6185,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - boolean providerRunning = false; - if (cpr != null && cpr.proc != null) { providerRunning = !cpr.proc.killed; @@ -7489,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"); } } @@ -7736,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) { @@ -7750,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) { @@ -8125,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--) { @@ -8187,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); @@ -8295,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; @@ -8416,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) { @@ -8428,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(); } @@ -8449,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); } @@ -9313,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 { @@ -9457,14 +8181,20 @@ public class ActivityManagerService extends IActivityManager.Stub // If at least 1/3 of our time since the last idle period has been spent // with RAM low, then we want to kill processes. boolean doKilling = lowRamSinceLastIdle > (timeSinceLastIdle/3); - - for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { - ProcessRecord proc = mLruProcesses.get(i); + // If the processes' memory has increased by more than 1% of the total memory, + // or 10 MB, whichever is greater, then the processes' are eligible to be killed. + final long totalMemoryInKb = getTotalMemory() / 1000; + final long memoryGrowthThreshold = + Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD); + + for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord proc = mProcessList.mLruProcesses.get(i); if (proc.notCachedSinceIdle) { if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) { if (doKilling && proc.initialIdlePss != 0 - && proc.lastPss > ((proc.initialIdlePss*3)/2)) { + && proc.lastPss > ((proc.initialIdlePss * 3) / 2) + && proc.lastPss > (proc.initialIdlePss + memoryGrowthThreshold)) { sb = new StringBuilder(128); sb.append("Kill"); sb.append(proc.processName); @@ -9588,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"); } } @@ -9975,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); } } @@ -10206,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; } @@ -10240,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"); @@ -10285,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); @@ -10295,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 @@ -10379,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); } } } @@ -10996,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) { @@ -11005,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); @@ -11029,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; } @@ -11085,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; } @@ -11149,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); } @@ -11178,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) { @@ -11336,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); @@ -11384,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); @@ -11400,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; } @@ -11434,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); } @@ -11452,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); } } } @@ -11466,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); } } } @@ -11476,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; } @@ -11614,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); @@ -11695,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:"); @@ -11715,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; } @@ -12356,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, @@ -14079,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); @@ -14219,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) { @@ -14243,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! @@ -14982,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, @@ -15399,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); } @@ -16389,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) { @@ -16421,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 { @@ -17602,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; @@ -17781,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; @@ -18334,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; @@ -18408,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--) { @@ -18469,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); @@ -18548,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--; @@ -18556,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)) { @@ -18567,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); @@ -18660,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!"); } @@ -18672,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(); @@ -18700,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; @@ -18778,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; @@ -19042,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<>(); @@ -19065,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; } @@ -19264,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( @@ -19283,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 */); @@ -19309,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); } @@ -19437,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); @@ -19567,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); } } @@ -19707,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; } @@ -19740,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; } @@ -19801,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); @@ -19822,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"); } } } @@ -20080,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; } @@ -20314,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) { @@ -20495,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 { @@ -20526,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. @@ -20574,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/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index d8afe0b58a55..865c774a2267 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -2284,6 +2284,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // We don't show starting window for overlay activities. return; } + if (pendingOptions != null + && pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { + // Don't show starting window when using shared element transition. + return; + } final CompatibilityInfo compatInfo = service.compatibilityInfoForPackageLocked(info.applicationInfo); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index e2bcb79b3837..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; } @@ -1239,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(); @@ -1299,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()) { @@ -1318,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. @@ -1354,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(); @@ -1381,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; @@ -1407,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)); @@ -1466,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 @@ -1484,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; @@ -1598,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 " @@ -1626,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..8ae5495cfa05 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). @@ -6247,20 +6215,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/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java index 23ae77d9a756..85ee7e65ffe7 100644 --- a/services/core/java/com/android/server/am/MemoryStatUtil.java +++ b/services/core/java/com/android/server/am/MemoryStatUtil.java @@ -46,6 +46,27 @@ final class MemoryStatUtil { static final String[] MEMORY_STAT_INTERESTING_NATIVE_PROCESSES = new String[]{ "/system/bin/statsd", // Stats daemon. "/system/bin/surfaceflinger", + "/system/bin/apexd", // APEX daemon. + "/system/bin/audioserver", + "/system/bin/cameraserver", + "/system/bin/drmserver", + "/system/bin/healthd", + "/system/bin/incidentd", + "/system/bin/installd", + "/system/bin/lmkd", // Low memory killer daemon. + "/system/bin/logd", + "media.codec", + "media.extractor", + "media.metrics", + "/system/bin/mediadrmserver", + "/system/bin/mediaserver", + "/system/bin/performanced", + "/system/bin/tombstoned", + "/system/bin/traced", // Perfetto. + "/system/bin/traced_probes", // Perfetto. + "webview_zygote", + "zygote", + "zygote64", }; static final int BYTES_IN_KILOBYTE = 1024; diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index b9c6fa6020c4..2dcddff2f442 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -288,12 +288,19 @@ public final class PendingIntentRecord extends IIntentSender.Stub { resolvedType = key.requestResolvedType; } + // Apply any launch flags from the ActivityOptions. This is to ensure that the caller + // can specify a consistent launch mode even if the PendingIntent is immutable + final ActivityOptions opts = ActivityOptions.fromBundle(options); + if (opts != null) { + finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + } + // Extract options before clearing calling identity mergedOptions = key.options; if (mergedOptions == null) { - mergedOptions = SafeActivityOptions.fromBundle(options); + mergedOptions = new SafeActivityOptions(opts); } else { - mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options)); + mergedOptions.setCallerOptions(opts); } if (whitelistDuration != null) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 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 1aa538d4abd1..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) { @@ -1353,9 +1407,10 @@ final class ProcessRecord implements WindowProcessListener { isInterestingToUserLocked() ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND); + final ProcessRecord parentPr = parentProcess != null + ? (ProcessRecord) parentProcess.mOwner : null; mService.addErrorToDropBox("anr", this, processName, activityShortComponentName, - parentShortComponentName, (ProcessRecord) parentProcess.mOwner, annotation, - cpuInfo, tracesFile, null); + parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null); if (mService.mActivityTaskManager.mController != null) { try { 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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index cd1a7c2be26c..f56d8e6a3ce2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4682,22 +4682,15 @@ public class AudioService extends IAudioService.Stub } } - @Override - public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) - { - mDeviceLogger.log((new AudioEventLogger.StringEvent( - "setHearingAidDeviceConnectionState state=" + state - + " addr=" + device.getAddress())).printLog(TAG)); - - setBluetoothHearingAidDeviceConnectionState( - device, state, false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE); - } - public int setBluetoothHearingAidDeviceConnectionState( BluetoothDevice device, int state, boolean suppressNoisyIntent, int musicDevice) { int delay; + mDeviceLogger.log((new AudioEventLogger.StringEvent( + "setHearingAidDeviceConnectionState state=" + state + + " addr=" + device.getAddress() + + " supprNoisy=" + suppressNoisyIntent)).printLog(TAG)); synchronized (mConnectedDevices) { if (!suppressNoisyIntent) { int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0; @@ -5887,6 +5880,7 @@ public class AudioService extends IAudioService.Stub address)); sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, null, 0); + setCurrentAudioRouteNameIfPossible(name); } private void onSendBecomingNoisyIntent() { @@ -5908,7 +5902,7 @@ public class AudioService extends IAudioService.Stub mConnectedDevices.remove( makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address)); // Remove A2DP routes as well - setCurrentAudioRouteName(null); + setCurrentAudioRouteNameIfPossible(null); if (mDockAddress == address) { mDockAddress = null; } @@ -5978,6 +5972,7 @@ public class AudioService extends IAudioService.Stub sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, AudioSystem.DEVICE_OUT_HEARING_AID, 0, mStreamStates[AudioSystem.STREAM_MUSIC], 0); + setCurrentAudioRouteNameIfPossible(name); } // must be called synchronized on mConnectedDevices @@ -5987,7 +5982,7 @@ public class AudioService extends IAudioService.Stub mConnectedDevices.remove( makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); // Remove Hearing Aid routes as well - setCurrentAudioRouteName(null); + setCurrentAudioRouteNameIfPossible(null); } // must be called synchronized on mConnectedDevices @@ -6032,7 +6027,6 @@ public class AudioService extends IAudioService.Stub } else { makeA2dpDeviceUnavailableNow(address); } - setCurrentAudioRouteName(null); } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { if (btDevice.isBluetoothDock()) { // this could be a reconnection after a transient disconnection @@ -6056,7 +6050,6 @@ public class AudioService extends IAudioService.Stub } makeA2dpDeviceAvailable(address, btDevice.getName(), "onSetA2dpSinkConnectionState"); - setCurrentAudioRouteName(btDevice.getAliasName()); } } } @@ -6108,25 +6101,35 @@ public class AudioService extends IAudioService.Stub if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { makeHearingAidDeviceUnavailable(address); - setCurrentAudioRouteName(null); } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { makeHearingAidDeviceAvailable(address, btDevice.getName(), "onSetHearingAidConnectionState"); - setCurrentAudioRouteName(btDevice.getAliasName()); } } } - private void setCurrentAudioRouteName(String name){ + private void setCurrentAudioRouteNameIfPossible(String name) { synchronized (mCurAudioRoutes) { if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) { - mCurAudioRoutes.bluetoothName = name; - sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES, - SENDMSG_NOOP, 0, 0, null, 0); + if (name != null || !isCurrentDeviceConnected()) { + mCurAudioRoutes.bluetoothName = name; + sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES, + SENDMSG_NOOP, 0, 0, null, 0); + } } } } + private boolean isCurrentDeviceConnected() { + for (int i = 0; i < mConnectedDevices.size(); i++) { + DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i); + if (TextUtils.equals(deviceSpec.mDeviceName, mCurAudioRoutes.bluetoothName)) { + return true; + } + } + return false; + } + private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice) { if (DEBUG_DEVICES) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index c16d3cd5b4a5..b148a2f3fff5 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -307,19 +307,19 @@ public class HdmiControlService extends SystemService { private final class CecMessageBuffer { private List<HdmiCecMessage> mBuffer = new ArrayList<>(); - public void bufferMessage(HdmiCecMessage message) { + public boolean bufferMessage(HdmiCecMessage message) { switch (message.getOpcode()) { case Constants.MESSAGE_ACTIVE_SOURCE: bufferActiveSource(message); - break; + return true; case Constants.MESSAGE_IMAGE_VIEW_ON: case Constants.MESSAGE_TEXT_VIEW_ON: bufferImageOrTextViewOn(message); - break; + return true; // Add here if new message that needs to buffer default: // Do not need to buffer messages other than above - break; + return false; } } @@ -906,10 +906,6 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly boolean handleCecCommand(HdmiCecMessage message) { assertRunOnServiceThread(); - if (!mAddressAllocated) { - mCecMessageBuffer.bufferMessage(message); - return true; - } int errorCode = mMessageValidator.isValid(message); if (errorCode != HdmiCecMessageValidator.OK) { // We'll not response on the messages with the invalid source or destination @@ -919,7 +915,12 @@ public class HdmiControlService extends SystemService { } return true; } - return dispatchMessageToLocalDevice(message); + + if (dispatchMessageToLocalDevice(message)) { + return true; + } + + return (!mAddressAllocated) ? mCecMessageBuffer.bufferMessage(message) : false; } void enableAudioReturnChannel(int portId, boolean enabled) { diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java index d347a9188dee..f7e871d0b645 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java @@ -50,8 +50,7 @@ final class SystemAudioAutoInitiationAction extends HdmiCecFeatureAction { @Override public void onSendCompleted(int error) { if (error != SendMessageResult.SUCCESS) { - tv().setSystemAudioMode(false); - finish(); + handleSystemAudioModeStatusTimeout(); } } }); diff --git a/services/core/java/com/android/server/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..348b8e69ab96 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)); } @@ -5842,8 +5840,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..19a62d91dbf7 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)); } } } @@ -1203,6 +1224,16 @@ public final class NotificationRecord { return getLogMaker(System.currentTimeMillis()); } + public LogMaker getItemLogMaker() { + return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM); + } + + public LogMaker getAdjustmentLogMaker() { + return getLogMaker() + .setCategory(MetricsEvent.NOTIFICATION_ITEM) + .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT); + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 760f1559c845..44b80c138346 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1212,27 +1212,89 @@ public class ZenModeHelper { } private final class Metrics extends Callback { - private static final String COUNTER_PREFIX = "dnd_mode_"; + private static final String COUNTER_MODE_PREFIX = "dnd_mode_"; + private static final String COUNTER_TYPE_PREFIX = "dnd_type_"; + private static final int DND_OFF = 0; + private static final int DND_ON_MANUAL = 1; + private static final int DND_ON_AUTOMATIC = 2; + private static final String COUNTER_RULE = "dnd_rule_count"; private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000; + // Total silence, alarms only, priority only private int mPreviousZenMode = -1; - private long mBeginningMs = 0L; + private long mModeLogTimeMs = 0L; + + private int mNumZenRules = -1; + private long mRuleCountLogTime = 0L; + + // automatic (1) vs manual (0) vs dnd off (2) + private int mPreviousZenType = -1; + private long mTypeLogTimeMs = 0L; @Override void onZenModeChanged() { emit(); } + @Override + void onConfigChanged() { + emit(); + } + private void emit() { mHandler.postMetricsTimer(); + emitZenMode(); + emitRules(); + emitDndType(); + } + + private void emitZenMode() { final long now = SystemClock.elapsedRealtime(); - final long since = (now - mBeginningMs); + final long since = (now - mModeLogTimeMs); if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) { if (mPreviousZenMode != -1) { - MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since); + MetricsLogger.count( + mContext, COUNTER_MODE_PREFIX + mPreviousZenMode, (int) since); } mPreviousZenMode = mZenMode; - mBeginningMs = now; + mModeLogTimeMs = now; + } + } + + private void emitRules() { + final long now = SystemClock.elapsedRealtime(); + final long since = (now - mRuleCountLogTime); + synchronized (mConfig) { + int numZenRules = mConfig.automaticRules.size(); + if (mNumZenRules != numZenRules + || since > MINIMUM_LOG_PERIOD_MS) { + if (mNumZenRules != -1) { + MetricsLogger.count(mContext, COUNTER_RULE, + numZenRules - mNumZenRules); + } + mNumZenRules = numZenRules; + + mRuleCountLogTime = since; + } + } + } + + private void emitDndType() { + final long now = SystemClock.elapsedRealtime(); + final long since = (now - mTypeLogTimeMs); + synchronized (mConfig) { + boolean dndOn = mZenMode != Global.ZEN_MODE_OFF; + int zenType = !dndOn ? DND_OFF + : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC; + if (zenType != mPreviousZenType + || since > MINIMUM_LOG_PERIOD_MS) { + if (mPreviousZenType != -1) { + MetricsLogger.count( + mContext, COUNTER_TYPE_PREFIX + mPreviousZenType, (int) since); + } + mTypeLogTimeMs = now; + mPreviousZenType = zenType; + } } } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 0a93653ede56..e6a018a9f39d 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -31,9 +31,11 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.PackagesProvider; import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider; +import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.media.RingtoneManager; @@ -1182,12 +1184,16 @@ public final class DefaultPermissionGrantPolicy { final int permissionGrantCount = permissionGrants.size(); for (int j = 0; j < permissionGrantCount; j++) { DefaultPermissionGrant permissionGrant = permissionGrants.get(j); + if (!isPermissionDangerous(permissionGrant.name)) { + Log.w(TAG, "Ignoring permission " + permissionGrant.name + + " which isn't dangerous"); + continue; + } if (permissions == null) { permissions = new ArraySet<>(); } else { permissions.clear(); } - permissions.add(permissionGrant.name); grantRuntimePermissions(pkg, permissions, permissionGrant.fixed, userId); } } @@ -1350,6 +1356,16 @@ public final class DefaultPermissionGrantPolicy { && pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; } + private boolean isPermissionDangerous(String name) { + try { + final PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(name, 0); + return (pi.getProtectionFlags() & PermissionInfo.PROTECTION_DANGEROUS) != 0; + } catch (NameNotFoundException e) { + // When unknown assume it's dangerous to be on the safe side + return true; + } + } + private static final class DefaultPermissionGrant { final String name; final boolean fixed; diff --git a/services/core/java/com/android/server/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/core/jni/Android.bp b/services/core/jni/Android.bp index 045f4ebf980f..15a3a1a3c378 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -99,6 +99,7 @@ cc_defaults { "libutils", "libhwui", "libbpf", + "libnetdbpf", "libnetdutils", "android.hardware.audio.common@2.0", "android.hardware.broadcastradio@1.0", diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp index 3302dea53506..649f1a56f011 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp @@ -30,8 +30,8 @@ #include <utils/Log.h> #include "android-base/unique_fd.h" -#include "bpf/BpfNetworkStats.h" #include "bpf/BpfUtils.h" +#include "netdbpf/BpfNetworkStats.h" using android::bpf::Stats; using android::bpf::hasBpfSupport; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 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/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 63e99c85d1b5..2dfb3751c021 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java @@ -62,14 +62,14 @@ public class ActivityManagerTest { private void testTaskIdsForUser(int userId) throws RemoteException { List<?> recentTasks = service.getRecentTasks(100, 0, userId).getList(); - assertThat(recentTasks).isNotNull(); - assertThat(recentTasks).isNotEmpty(); - 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); + 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); + } } } } 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/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/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/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 8454d12ac317..d9e71679ced8 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -18,6 +18,7 @@ package android.provider; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.job.JobService; @@ -2889,100 +2890,131 @@ public final class Telephony { public static final String SUBSCRIPTION_ID = "sub_id"; /** - * The profile_id to which the APN saved in modem + * The profile_id to which the APN saved in modem. * <p>Type: INTEGER</p> *@hide */ public static final String PROFILE_ID = "profile_id"; /** - * Is the apn setting to be set in modem - * <P>Type: INTEGER (boolean)</P> + * If set to {@code true}, then the APN setting will persist to the modem. + * <p>Type: INTEGER (boolean)</p> *@hide */ + @SystemApi public static final String MODEM_COGNITIVE = "modem_cognitive"; /** - * The max connections of this apn + * The max connections of this APN. * <p>Type: INTEGER</p> *@hide */ + @SystemApi public static final String MAX_CONNS = "max_conns"; /** - * The wait time for retry of the apn + * The wait time for retry of the APN. * <p>Type: INTEGER</p> *@hide */ + @SystemApi public static final String WAIT_TIME = "wait_time"; /** - * The time to limit max connection for the apn + * The time to limit max connection for the APN. * <p>Type: INTEGER</p> *@hide */ + @SystemApi public static final String MAX_CONNS_TIME = "max_conns_time"; /** - * The MTU size of the mobile interface to which the APN connected + * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected. * <p>Type: INTEGER </p> * @hide */ + @SystemApi public static final String MTU = "mtu"; /** - * Is this APN added/edited/deleted by a user or carrier? + * APN edit status. APN could be added/edited/deleted by a user or carrier. * <p>Type: INTEGER </p> * @hide */ + @SystemApi public static final String EDITED = "edited"; /** - * Is this APN visible to the user? - * <p>Type: INTEGER (boolean) </p> + * {@code true} if this APN visible to the user, {@code false} otherwise. + * <p>Type: INTEGER (boolean)</p> * @hide */ + @SystemApi public static final String USER_VISIBLE = "user_visible"; /** - * Is the user allowed to edit this APN? - * <p>Type: INTEGER (boolean) </p> + * {@code true} if the user allowed to edit this APN, {@code false} otherwise. + * <p>Type: INTEGER (boolean)</p> * @hide */ + @SystemApi public static final String USER_EDITABLE = "user_editable"; /** - * Following are possible values for the EDITED field + * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to + * edit. + * <p>Type: INTEGER </p> * @hide */ + @SystemApi public static final int UNEDITED = 0; + /** - * @hide + * {@link #EDITED APN edit status} indicates that this APN has been edited by users. + * <p>Type: INTEGER </p> + * @hide */ + @SystemApi public static final int USER_EDITED = 1; + /** - * @hide + * {@link #EDITED APN edit status} indicates that this APN has been deleted by users. + * <p>Type: INTEGER </p> + * @hide */ + @SystemApi public static final int USER_DELETED = 2; + /** - * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted - * by the user is still present in the new APN database and therefore must remain tagged - * as user deleted rather than completely removed from the database + * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry + * deleted by the user is still present in the new APN database and therefore must remain + * tagged as user deleted rather than completely removed from the database. * @hide */ public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3; + /** - * @hide + * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers. + * <p>Type: INTEGER </p> + * @hide */ + @SystemApi public static final int CARRIER_EDITED = 4; + /** - * CARRIER_DELETED values are currently not used as there is no usecase. If they are used, + * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers. + * CARRIER_DELETED values are currently not used as there is no use case. If they are used, * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED. + * <p>Type: INTEGER </p> * @hide */ public static final int CARRIER_DELETED = 5; + /** - * @hide + * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry + * deleted by the carrier is still present in the new APN database and therefore must remain + * tagged as user deleted rather than completely removed from the database. + * @hide */ public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6; @@ -3011,16 +3043,20 @@ public final class Telephony { * The APN set id. When the user manually selects an APN or the framework sets an APN as * preferred, all APNs with the same set id as the selected APN should be prioritized over * APNs in other sets. + * <p>Type: INTEGER</p> * @hide */ + @SystemApi public static final String APN_SET_ID = "apn_set_id"; /** - * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the - * user manually selects an APN with no set set, there is no need to prioritize any specific - * APN set ids. + * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a + * set. If the user manually selects an APN with no set set, there is no need to prioritize + * any specific APN set ids. + * <p>Type: INTEGER</p> * @hide */ + @SystemApi public static final int NO_SET_SET = 0; } @@ -3524,6 +3560,18 @@ public final class Telephony { public static final String CARRIER_ID = "carrier_id"; /** + * A unique mno carrier id. mno carrier shares the same {@link All#MCCMNC} as carrier id + * and can be solely identified by {@link All#MCCMNC} only. If there is no such mno + * carrier, then mno carrier id equals to {@link #CARRIER_ID carrier id}. + * + * <p>mno carrier id can be used as fallback id. When the exact carrier id configurations + * are not found, usually fall back to its mno carrier id. + * <P>Type: INTEGER </P> + * @hide + */ + public static final String MNO_CARRIER_ID = "mno_carrier_id"; + + /** * Contains mappings between matching rules with carrier id for all carriers. * @hide */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index fac1943478b2..b0997f1f24ea 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1202,6 +1202,20 @@ public class CarrierConfigManager { "always_show_data_rat_icon_bool"; /** + * Boolean indicating if default data account should show LTE or 4G icon + * @hide + */ + public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = + "show_4g_for_lte_data_icon_bool"; + + /** + * Boolean indicating if lte+ icon should be shown if available + * @hide + */ + public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = + "hide_lte_plus_data_icon_bool"; + + /** * Boolean to decide whether to show precise call failed cause to user * @hide */ @@ -1965,6 +1979,31 @@ public class CarrierConfigManager { public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool"; /** + * Indicates if the carrier supports auto-upgrading a call to RTT when receiving a call from a + * RTT-supported device. + * @hide + */ + public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool"; + + /** + * Indicates if the carrier supports RTT during a video call. + * @hide + */ + public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool"; + + /** + * Indicates if the carrier supports upgrading a voice call to an RTT call during the call. + * @hide + */ + public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool"; + + /** + * Indicates if the carrier supports downgrading a RTT call to a voice call during the call. + * @hide + */ + public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool"; + + /** * The flag to disable the popup dialog which warns the user of data charges. * @hide */ @@ -2505,6 +2544,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false); sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false); + sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true); + sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false); sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false); sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY, diff --git a/telephony/java/android/telephony/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 8f78c346e422..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"; @@ -1163,6 +1164,16 @@ public class TelephonyManager { public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID"; /** + * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates + * the updated mno carrier id of the current subscription. + * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or + * the carrier cannot be identified. + * + *@hide + */ + public static final String EXTRA_MNO_CARRIER_ID = "android.telephony.extra.MNO_CARRIER_ID"; + + /** * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which * indicates the updated carrier name of the current subscription. * {@see TelephonyManager#getSimCarrierIdName()} @@ -1221,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 @@ -1268,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. @@ -1297,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 * @@ -1329,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) @@ -1345,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 */ @@ -1398,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) @@ -1413,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 */ @@ -1722,7 +1765,7 @@ public class TelephonyManager { } /** {@hide} */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @UnsupportedAppUsage private int getPhoneTypeFromProperty(int phoneId) { String type = getTelephonyProperty(phoneId, TelephonyProperties.CURRENT_ACTIVE_PHONE, null); @@ -1906,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, ""); @@ -1934,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); @@ -2258,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(); @@ -2294,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(); @@ -2777,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); } @@ -2791,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)) { @@ -2820,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); @@ -2834,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, ""); @@ -2861,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); @@ -2891,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); @@ -2913,11 +2956,11 @@ public class TelephonyManager { * unavailable. * * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or - * profile owner, or that the calling app has carrier privileges (see {@link - * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the - * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> - * Work profiles</a>. Profile owner access is deprecated and will be removed in a future - * release. + * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier + * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a + * managed profile on the device; for more details see <a + * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner + * access is deprecated and will be removed in a future release. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -2930,11 +2973,11 @@ public class TelephonyManager { * unavailable. * * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or - * profile owner, or that the calling app has carrier privileges (see {@link - * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the - * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> - * Work profiles</a>. Profile owner access is deprecated and will be removed in a future - * release. + * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier + * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a + * managed profile on the device; for more details see <a + * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner + * access is deprecated and will be removed in a future release. * * @param subId for which Sim Serial number is returned * @hide @@ -3075,11 +3118,11 @@ public class TelephonyManager { * Return null if it is unavailable. * * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or - * profile owner, or that the calling app has carrier privileges (see {@link - * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the - * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> - * Work profiles</a>. Profile owner access is deprecated and will be removed in a future - * release. + * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier + * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a + * managed profile on the device; for more details see <a + * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner + * access is deprecated and will be removed in a future release. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -3093,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(); @@ -3488,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(); @@ -4421,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)); } @@ -7394,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; } @@ -7999,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(); @@ -8333,20 +8378,31 @@ public class TelephonyManager { } /** - * Action set from carrier signalling broadcast receivers to enable/disable metered apns - * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required - * @param subId the subscription ID that this action applies to. - * @param enabled control enable or disable metered apns. + * Used to enable or disable carrier data by the system based on carrier signalling or + * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to + * user settings, carrier data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param enabled control enable or disable carrier data. * @hide */ - public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) { + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setCarrierDataEnabled(boolean enabled) { try { ITelephony service = getITelephony(); if (service != null) { - service.carrierActionSetMeteredApnsEnabled(subId, enabled); + service.carrierActionSetMeteredApnsEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e); + Log.e(TAG, "Error calling ITelephony#setCarrierDataEnabled", e); } } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index eb144f9053be..aabefe324d82 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -257,7 +257,7 @@ public class ApnSetting implements Parcelable { private final int mProfileId; - private final boolean mModemCognitive; + private final boolean mPersistent; private final int mMaxConns; private final int mWaitTime; private final int mMaxConnsTime; @@ -290,13 +290,13 @@ public class ApnSetting implements Parcelable { } /** - * Returns if the APN setting is to be set in modem. + * Returns if the APN setting is persistent on the modem. * * @return is the APN setting to be set in modem * @hide */ - public boolean getModemCognitive() { - return mModemCognitive; + public boolean isPersistent() { + return mPersistent; } /** @@ -616,7 +616,7 @@ public class ApnSetting implements Parcelable { this.mCarrierEnabled = builder.mCarrierEnabled; this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask; this.mProfileId = builder.mProfileId; - this.mModemCognitive = builder.mModemCognitive; + this.mPersistent = builder.mModemCognitive; this.mMaxConns = builder.mMaxConns; this.mWaitTime = builder.mWaitTime; this.mMaxConnsTime = builder.mMaxConnsTime; @@ -740,7 +740,7 @@ public class ApnSetting implements Parcelable { apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser, apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol, apn.mCarrierEnabled, apn.mNetworkTypeBitmask, - apn.mProfileId, apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, + apn.mProfileId, apn.mPersistent, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData, apn.mApnSetId); } @@ -947,7 +947,7 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(PROTOCOL_INT_MAP.get(mRoamingProtocol)); sb.append(", ").append(mCarrierEnabled); sb.append(", ").append(mProfileId); - sb.append(", ").append(mModemCognitive); + sb.append(", ").append(mPersistent); sb.append(", ").append(mMaxConns); sb.append(", ").append(mWaitTime); sb.append(", ").append(mMaxConnsTime); @@ -1029,7 +1029,7 @@ public class ApnSetting implements Parcelable { && Objects.equals(mMmsc, other.mMmsc) && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) - && Objects.equals(mProxyPort,other.mProxyPort) + && Objects.equals(mProxyPort, other.mProxyPort) && Objects.equals(mUser, other.mUser) && Objects.equals(mPassword, other.mPassword) && Objects.equals(mAuthType, other.mAuthType) @@ -1038,7 +1038,7 @@ public class ApnSetting implements Parcelable { && Objects.equals(mRoamingProtocol, other.mRoamingProtocol) && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) && Objects.equals(mProfileId, other.mProfileId) - && Objects.equals(mModemCognitive, other.mModemCognitive) + && Objects.equals(mPersistent, other.mPersistent) && Objects.equals(mMaxConns, other.mMaxConns) && Objects.equals(mWaitTime, other.mWaitTime) && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) @@ -1080,11 +1080,11 @@ public class ApnSetting implements Parcelable { && Objects.equals(mPassword, other.mPassword) && Objects.equals(mAuthType, other.mAuthType) && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) - && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol)) + && (isDataRoaming || Objects.equals(mProtocol, other.mProtocol)) && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol)) && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) && Objects.equals(mProfileId, other.mProfileId) - && Objects.equals(mModemCognitive, other.mModemCognitive) + && Objects.equals(mPersistent, other.mPersistent) && Objects.equals(mMaxConns, other.mMaxConns) && Objects.equals(mWaitTime, other.mWaitTime) && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index e8597b221391..da4822cc1d14 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -68,17 +68,15 @@ public final class DataProfile implements Parcelable { private final int mMtu; - private final String mMvnoType; + private final boolean mPersistent; - private final String mMvnoMatchData; + private final boolean mPreferred; - private final boolean mModemCognitive; - - public DataProfile(int profileId, String apn, String protocol, int authType, - String userName, String password, int type, int maxConnsTime, int maxConns, - int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol, - int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData, - boolean modemCognitive) { + /** @hide */ + public DataProfile(int profileId, String apn, String protocol, int authType, String userName, + String password, int type, int maxConnsTime, int maxConns, int waitTime, + boolean enabled, int supportedApnTypesBitmap, String roamingProtocol, + int bearerBitmap, int mtu, boolean persistent, boolean preferred) { this.mProfileId = profileId; this.mApn = apn; @@ -100,11 +98,11 @@ public final class DataProfile implements Parcelable { this.mRoamingProtocol = roamingProtocol; this.mBearerBitmap = bearerBitmap; this.mMtu = mtu; - this.mMvnoType = mvnoType; - this.mMvnoMatchData = mvnoMatchData; - this.mModemCognitive = modemCognitive; + this.mPersistent = persistent; + this.mPreferred = preferred; } + /** @hide */ public DataProfile(Parcel source) { mProfileId = source.readInt(); mApn = source.readString(); @@ -121,9 +119,8 @@ public final class DataProfile implements Parcelable { mRoamingProtocol = source.readString(); mBearerBitmap = source.readInt(); mMtu = source.readInt(); - mMvnoType = source.readString(); - mMvnoMatchData = source.readString(); - mModemCognitive = source.readBoolean(); + mPersistent = source.readBoolean(); + mPreferred = source.readBoolean(); } /** @@ -207,23 +204,17 @@ public final class DataProfile implements Parcelable { public int getMtu() { return mMtu; } /** - * @return The MVNO type: possible values are "imsi", "gid", "spn". - */ - public String getMvnoType() { return mMvnoType; } - - /** - * @return The MVNO match data. For example, - * SPN: A MOBILE, BEN NL, ... - * IMSI: 302720x94, 2060188, ... - * GID: 4E, 33, ... + * @return {@code true} if modem must persist this data profile. */ - public String getMvnoMatchData() { return mMvnoMatchData; } + public boolean isPersistent() { return mPersistent; } /** - * @return True if the data profile was sent to the modem through setDataProfile earlier. + * @return {@code true} if this data profile was used to bring up the last default + * (i.e internet) data connection successfully. */ - public boolean isModemCognitive() { return mModemCognitive; } + public boolean isPreferred() { return mPreferred; } + /** @hide */ @Override public int describeContents() { return 0; @@ -233,11 +224,11 @@ public final class DataProfile implements Parcelable { public String toString() { return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType + "/" + (Build.IS_USER ? "***/***/***" : - (mApn + "/" + mUserName + "/" + mPassword)) - + "/" + mType + "/" + mMaxConnsTime - + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/" - + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/" - + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive; + (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/" + + mMaxConnsTime + "/" + mMaxConns + "/" + + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmap + "/" + + mRoamingProtocol + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/" + + mPreferred; } @Override @@ -246,6 +237,7 @@ public final class DataProfile implements Parcelable { return (o == this || toString().equals(o.toString())); } + /** @hide */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mProfileId); @@ -263,11 +255,11 @@ public final class DataProfile implements Parcelable { dest.writeString(mRoamingProtocol); dest.writeInt(mBearerBitmap); dest.writeInt(mMtu); - dest.writeString(mMvnoType); - dest.writeString(mMvnoMatchData); - dest.writeBoolean(mModemCognitive); + dest.writeBoolean(mPersistent); + dest.writeBoolean(mPreferred); } + /** @hide */ public static final Parcelable.Creator<DataProfile> CREATOR = new Parcelable.Creator<DataProfile>() { @Override diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index d6a08543b9cd..bdba8c860db0 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -33,7 +33,7 @@ import java.util.Set; * A parcelable class that wraps and retrieves the information of number, service category(s) and * country code for a specific emergency number. */ -public final class EmergencyNumber implements Parcelable { +public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNumber> { private static final String LOG_TAG = "EmergencyNumber"; @@ -235,20 +235,22 @@ public final class EmergencyNumber implements Parcelable { } /** - * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of - * the emergency number. + * Returns the bitmask of emergency service categories of the emergency number. * - * @return bitmask of the emergency service categories {@link EmergencyServiceCategories} + * @return bitmask of the emergency service categories */ public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() { return mEmergencyServiceCategoryBitmask; } /** - * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency - * number. + * Returns the emergency service categories of the emergency number. * - * @return a list of the emergency service categories {@link EmergencyServiceCategories} + * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only + * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in + * all categories. + * + * @return a list of the emergency service categories */ public List<Integer> getEmergencyServiceCategories() { List<Integer> categories = new ArrayList<>(); @@ -276,34 +278,37 @@ public final class EmergencyNumber implements Parcelable { } /** - * Checks if the emergency number is in the specified emergency service category(s) - * {@link EmergencyServiceCategories}. + * Checks if the emergency number is in the supplied emergency service category(s). * - * @return {@code true} if the emergency number is in the specified emergency service - * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise. + * @param categories - the supplied emergency service categories * - * @param categories - emergency service categories {@link EmergencyServiceCategories} + * @return {@code true} if the emergency number is in the specified emergency service + * category(s) or if its emergency service category is + * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise. */ public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) { if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) { return serviceUnspecified(); } + if (serviceUnspecified()) { + return true; + } return (mEmergencyServiceCategoryBitmask & categories) == categories; } /** - * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number. + * Returns the bitmask of the sources of the emergency number. * - * @return bitmask of the emergency number sources {@link EmergencyNumberSources} + * @return bitmask of the emergency number sources */ public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() { return mEmergencyNumberSourceBitmask; } /** - * Returns a list of {@link EmergencyNumberSources} of the emergency number. + * Returns a list of sources of the emergency number. * - * @return a list of {@link EmergencyNumberSources} + * @return a list of emergency number sources */ public List<Integer> getEmergencyNumberSources() { List<Integer> sources = new ArrayList<>(); @@ -316,13 +321,12 @@ public final class EmergencyNumber implements Parcelable { } /** - * Checks if the emergency number is from the specified emergency number source(s) - * {@link EmergencyNumberSources}. + * Checks if the emergency number is from the specified emergency number source(s). * * @return {@code true} if the emergency number is from the specified emergency number - * source(s) {@link EmergencyNumberSources}; {@code false} otherwise. + * source(s); {@code false} otherwise. * - * @param sources - {@link EmergencyNumberSources} + * @param sources - the supplied emergency number sources */ public boolean isFromSources(@EmergencyNumberSources int sources) { return (mEmergencyNumberSourceBitmask & sources) == sources; @@ -359,6 +363,62 @@ public final class EmergencyNumber implements Parcelable { return (o == this || toString().equals(o.toString())); } + /** + * Calculate the score for display priority. + * + * A higher display priority score means the emergency number has a higher display priority. + * The score is higher if the source is defined for a higher display priority. + * + * The priority of sources are defined as follows: + * EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING > + * EMERGENCY_NUMBER_SOURCE_SIM > + * EMERGENCY_NUMBER_SOURCE_DEFAULT > + * EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG + * + */ + private int getDisplayPriorityScore() { + int score = 0; + if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) { + score += 1 << 4; + } + if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) { + score += 1 << 3; + } + // TODO add a score if the number comes from Google's emergency number database + if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) { + score += 1 << 1; + } + if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) { + score += 1 << 0; + } + return score; + } + + /** + * Compare the display priority for this emergency number and the supplied emergency number. + * + * @param emergencyNumber the supplied emergency number + * @return a negative value if the supplied emergency number has a lower display priority; + * a positive value if the supplied emergency number has a higher display priority; + * 0 if both have equal display priority. + */ + @Override + public int compareTo(EmergencyNumber emergencyNumber) { + if (this.getDisplayPriorityScore() + > emergencyNumber.getDisplayPriorityScore()) { + return -1; + } else if (this.getDisplayPriorityScore() + < emergencyNumber.getDisplayPriorityScore()) { + return 1; + } else { + /** + * TODO if both numbers have the same display priority score, the number matches the + * Google's emergency number database has a higher display priority. + */ + return 0; + } + } + public static final Parcelable.Creator<EmergencyNumber> CREATOR = new Parcelable.Creator<EmergencyNumber>() { @Override diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index 8d18ae8ed281..f2d0cbf13cc9 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -16,22 +16,19 @@ package android.telephony.ims; +import android.annotation.IntDef; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.telecom.Log; import android.telephony.Rlog; -/* - * This file contains all the api's through which - * information received in Dialog Event Package can be - * queried - */ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** - * Parcelable object to handle MultiEndpoint Dialog Information + * Parcelable object to handle MultiEndpoint Dialog Event Package Information. * @hide */ @SystemApi @@ -40,8 +37,39 @@ public final class ImsExternalCallState implements Parcelable { private static final String TAG = "ImsExternalCallState"; // Dialog States + /** + * The external call is in the confirmed dialog state. + */ public static final int CALL_STATE_CONFIRMED = 1; + /** + * The external call is in the terminated dialog state. + */ public static final int CALL_STATE_TERMINATED = 2; + + /**@hide*/ + @IntDef(flag = true, + value = { + CALL_STATE_CONFIRMED, + CALL_STATE_TERMINATED + }, + prefix = "CALL_STATE_") + @Retention(RetentionPolicy.SOURCE) + public @interface ExternalCallState {} + + /**@hide*/ + @IntDef(flag = true, + value = { + ImsCallProfile.CALL_TYPE_VOICE, + ImsCallProfile.CALL_TYPE_VT_TX, + ImsCallProfile.CALL_TYPE_VT_RX, + ImsCallProfile.CALL_TYPE_VT + }, + prefix = "CALL_TYPE_") + @Retention(RetentionPolicy.SOURCE) + public @interface ExternalCallType {} + + + // Dialog Id private int mCallId; // Number @@ -58,10 +86,9 @@ public final class ImsExternalCallState implements Parcelable { public ImsExternalCallState() { } - /** @hide */ - @UnsupportedAppUsage - public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState, - int callType, boolean isCallheld) { + /**@hide*/ + public ImsExternalCallState(int callId, Uri address, boolean isPullable, + @ExternalCallState int callState, int callType, boolean isCallheld) { mCallId = callId; mAddress = address; mIsPullable = isPullable; @@ -71,9 +98,10 @@ public final class ImsExternalCallState implements Parcelable { Rlog.d(TAG, "ImsExternalCallState = " + this); } - /** @hide */ + /**@hide*/ public ImsExternalCallState(int callId, Uri address, Uri localAddress, - boolean isPullable, int callState, int callType, boolean isCallheld) { + boolean isPullable, @ExternalCallState int callState, int callType, + boolean isCallheld) { mCallId = callId; mAddress = address; mLocalAddress = localAddress; @@ -84,6 +112,31 @@ public final class ImsExternalCallState implements Parcelable { Rlog.d(TAG, "ImsExternalCallState = " + this); } + /** + * Create a new ImsExternalCallState instance to contain Multiendpoint Dialog information. + * @param callId The unique ID of the call, which will be used to identify this external + * connection. + * @param address A {@link Uri} containing the remote address of this external connection. + * @param localAddress A {@link Uri} containing the local address information. + * @param isPullable A flag determining if this external connection can be pulled to the current + * device. + * @param callState The state of the external call. + * @param callType The type of external call. + * @param isCallheld A flag determining if the external connection is currently held. + */ + public ImsExternalCallState(String callId, Uri address, Uri localAddress, + boolean isPullable, @ExternalCallState int callState, @ExternalCallType int callType, + boolean isCallheld) { + mCallId = getIdForString(callId); + mAddress = address; + mLocalAddress = localAddress; + mIsPullable = isPullable; + mCallState = callState; + mCallType = callType; + mIsHeld = isCallheld; + Rlog.d(TAG, "ImsExternalCallState = " + this); + } + /** @hide */ public ImsExternalCallState(Parcel in) { mCallId = in.readInt(); @@ -135,7 +188,9 @@ public final class ImsExternalCallState implements Parcelable { return mAddress; } - /** @hide */ + /** + * @return A {@link Uri} containing the local address from the Multiendpoint Dialog Information. + */ public Uri getLocalAddress() { return mLocalAddress; } @@ -144,11 +199,11 @@ public final class ImsExternalCallState implements Parcelable { return mIsPullable; } - public int getCallState() { + public @ExternalCallState int getCallState() { return mCallState; } - public int getCallType() { + public @ExternalCallType int getCallType() { return mCallType; } @@ -166,4 +221,15 @@ public final class ImsExternalCallState implements Parcelable { ", mCallType = " + mCallType + ", mIsHeld = " + mIsHeld + "}"; } + + private int getIdForString(String idString) { + try { + return Integer.parseInt(idString); + } catch (NumberFormatException e) { + // In the case that there are alphanumeric characters, we will create a hash of the + // String value as a backup. + // TODO: Modify call IDs to use Strings as keys instead of integers in telephony/telecom + return idString.hashCode(); + } + } } diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java new file mode 100644 index 000000000000..c9cf473bb482 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -0,0 +1,760 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.content.Context; +import android.net.Uri; +import android.os.Binder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.telephony.SubscriptionManager; +import android.telephony.ims.aidl.IImsCapabilityCallback; +import android.telephony.ims.aidl.IImsRegistrationCallback; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; + +import com.android.internal.telephony.ITelephony; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated + * subscription. + * + * Allows a user to query the IMS MmTel feature information for a subscription, register for + * registration and MmTel capability status callbacks, as well as query/modify user settings for the + * associated subscription. + * + * @see #createForSubscriptionId(Context, int) + * @hide + */ +public class ImsMmTelManager { + + private static final String TAG = "ImsMmTelManager"; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "WIFI_MODE_", value = { + WIFI_MODE_WIFI_ONLY, + WIFI_MODE_CELLULAR_PREFERRED, + WIFI_MODE_WIFI_PREFERRED + }) + public @interface WiFiCallingMode {} + + /** + * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE + * registration if signal quality degrades. + * @hide + */ + @SystemApi + public static final int WIFI_MODE_WIFI_ONLY = 0; + + /** + * Prefer registering for IMS over LTE if LTE signal quality is high enough. + * @hide + */ + @SystemApi + public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; + + /** + * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough. + * @hide + */ + @SystemApi + public static final int WIFI_MODE_WIFI_PREFERRED = 2; + + /** + * Callback class for receiving Registration callback events. + * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback) + * @see #removeImsRegistrationCallback(RegistrationCallback) + */ + public static class RegistrationCallback { + + private static class RegistrationBinder extends IImsRegistrationCallback.Stub { + + private final RegistrationCallback mLocalCallback; + private Executor mExecutor; + + RegistrationBinder(RegistrationCallback localCallback) { + mLocalCallback = localCallback; + } + + @Override + public void onRegistered(int imsRadioTech) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech))); + } + + @Override + public void onRegistering(int imsRadioTech) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech))); + } + + @Override + public void onDeregistered(ImsReasonInfo info) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onDeregistered(info))); + } + + @Override + public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> + mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info))); + } + + @Override + public void onSubscriberAssociatedUriChanged(Uri[] uris) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> + mLocalCallback.onSubscriberAssociatedUriChanged(uris))); + } + + private void setExecutor(Executor executor) { + mExecutor = executor; + } + } + + private final RegistrationBinder mBinder = new RegistrationBinder(this); + + /** + * Notifies the framework when the IMS Provider is registered to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is trying to register the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is deregistered from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + public void onDeregistered(ImsReasonInfo info) { + } + + /** + * A failure has occurred when trying to handover registration to another technology type, + * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech} + * + * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has + * failed + * @param info A {@link ImsReasonInfo} that identifies the reason for failure. + */ + public void onTechnologyChangeFailed( + @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) { + } + + /** + * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when + * it changes. Per RFC3455, an associated URI is a URI that the service provider has + * allocated to a user for their own usage. A user's phone number is typically one of the + * associated URIs. + * @param uris new array of subscriber {@link Uri}s that are associated with this IMS + * subscription. + * @hide + */ + public void onSubscriberAssociatedUriChanged(Uri[] uris) { + } + + /**@hide*/ + public final IImsRegistrationCallback getBinder() { + return mBinder; + } + + /**@hide*/ + //Only exposed as public for compatibility with deprecated ImsManager APIs. + public void setExecutor(Executor executor) { + mBinder.setExecutor(executor); + } + } + + /** + * Receives IMS capability status updates from the ImsService. + * + * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback) + * @see #removeMmTelCapabilityCallback(CapabilityCallback) + */ + public static class CapabilityCallback { + + private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + + private final CapabilityCallback mLocalCallback; + private Executor mExecutor; + + CapabilityBinder(CapabilityCallback c) { + mLocalCallback = c; + } + + @Override + public void onCapabilitiesStatusChanged(int config) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged( + new MmTelFeature.MmTelCapabilities(config)))); + } + + @Override + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + // This is not used for public interfaces. + } + + @Override + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + @ImsFeature.ImsCapabilityError int reason) { + // This is not used for public interfaces + } + + private void setExecutor(Executor executor) { + mExecutor = executor; + } + } + + private final CapabilityBinder mBinder = new CapabilityBinder(this); + + /** + * The status of the feature's capabilities has changed to either available or unavailable. + * If unavailable, the feature is not able to support the unavailable capability at this + * time. + * + * @param capabilities The new availability of the capabilities. + */ + public void onCapabilitiesStatusChanged( + MmTelFeature.MmTelCapabilities capabilities) { + } + + /**@hide*/ + public final IImsCapabilityCallback getBinder() { + return mBinder; + } + + /**@hide*/ + // Only exposed as public method for compatibility with deprecated ImsManager APIs. + // TODO: clean up dependencies and change back to private visibility. + public void setExecutor(Executor executor) { + mBinder.setExecutor(executor); + } + } + + private Context mContext; + private int mSubId; + + /** + * Create an instance of ImsManager for the subscription id specified. + * + * @param context + * @param subId The ID of the subscription that this ImsManager will use. + * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() + * @throws IllegalArgumentException if the subscription is invalid or + * the subscription ID is not an active subscription. + */ + public static ImsMmTelManager createForSubscriptionId(Context context, int subId) { + if (!SubscriptionManager.isValidSubscriptionId(subId) + || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) { + throw new IllegalArgumentException("Invalid subscription ID"); + } + + return new ImsMmTelManager(context, subId); + } + + private ImsMmTelManager(Context context, int subId) { + mContext = context; + mSubId = subId; + } + + /** + * Registers a {@link RegistrationCallback} with the system, which will provide registration + * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use + * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed + * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up + * after a subscription is removed. + * @param executor The executor the callback events should be run on. + * @param c The {@link RegistrationCallback} to be added. + * @see #removeImsRegistrationCallback(RegistrationCallback) + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public void addImsRegistrationCallback(@CallbackExecutor Executor executor, + @NonNull RegistrationCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + try { + getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning + * up to avoid memory leaks or when the subscription is removed. + * @param c The {@link RegistrationCallback} to be removed. + * @see SubscriptionManager.OnSubscriptionsChangedListener + * @see #addImsRegistrationCallback(Executor, RegistrationCallback) + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + try { + getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability + * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. + * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to + * subscription changed events and call + * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription + * is removed. + * @param executor The executor the callback events should be run on. + * @param c The MmTel {@link CapabilityCallback} to be registered. + * @see #removeMmTelCapabilityCallback(CapabilityCallback) + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor, + @NonNull CapabilityCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + try { + getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning + * up to avoid memory leaks. + * @param c The MmTel {@link CapabilityCallback} to be removed. + * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); + } + try { + getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Query the user's setting for whether or not to use MmTel capabilities over IMS, + * such as voice and video, depending on carrier configuration for the current subscription. + * @see #setAdvancedCallingSetting(boolean) + * @return true if the user’s setting for advanced calling is enabled and false otherwise. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isAdvancedCallingSettingEnabled() { + try { + return getITelephony().isAdvancedCallingSettingEnabled(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to + * enable MmTel IMS features, such as voice and video calling, depending on the carrier + * configuration for the current subscription. Modifying this value may also trigger an IMS + * registration or deregistration, depending on the new value. + * @see #isAdvancedCallingEnabled() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setAdvancedCallingSetting(boolean isEnabled) { + try { + getITelephony().setAdvancedCallingSetting(mSubId, isEnabled); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Query the IMS MmTel capability for a given registration technology. This does not + * necessarily mean that we are registered and the capability is available, but rather the + * subscription is capable of this service over IMS. + * + * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL + * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL + * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL + * @see #isAvailable(int, int) + * + * @param imsRegTech The IMS registration technology, can be one of the following: + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * @param capability The IMS MmTel capability to query, can be one of the following: + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @return {@code true} if the MmTel IMS capability is capable for this subscription, false + * otherwise. + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { + try { + return getITelephony().isCapable(mSubId, capability, imsRegTech, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Query the availability of an IMS MmTel capability for a given registration technology. If + * a capability is available, IMS is registered and the service is currently available over IMS. + * + * @see #isCapable(int, int) + * + * @param imsRegTech The IMS registration technology, can be one of the following: + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * @param capability The IMS MmTel capability to query, can be one of the following: + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @return {@code true} if the MmTel IMS capability is available for this subscription, false + * otherwise. + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { + try { + return getITelephony().isAvailable(mSubId, capability, imsRegTech, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * The user's setting for whether or not they have enabled the "Video Calling" setting. + * @return true if the user’s “Video Calling” setting is currently enabled. + * @see #setVtSetting(boolean) + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public boolean isVtSettingEnabled() { + try { + return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Change the user's setting for Video Telephony and enable the Video Telephony capability. + * @see #isVtSettingEnabled() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setVtSetting(boolean isEnabled) { + try { + getITelephony().setVtSetting(mSubId, isEnabled); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. + * @see #setVoWiFiSetting(boolean) + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isVoWiFiSettingEnabled() { + try { + return getITelephony().isVoWiFiSettingEnabled(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Sets the user's setting for whether or not Voice over WiFi is enabled. + * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise= + * @see #isVoWiFiSettingEnabled() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setVoWiFiSetting(boolean isEnabled) { + try { + getITelephony().setVoWiFiSetting(mSubId, isEnabled); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * @return true if the user's setting for Voice over WiFi while roaming is enabled, false + * if disabled. + * @see #setVoWiFiRoamingSetting(boolean) + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isVoWiFiRoamingSettingEnabled() { + try { + return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Change the user's setting for Voice over WiFi while roaming. + * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled, + * false otherwise. + * @see #isVoWiFiRoamingSettingEnabled() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setVoWiFiRoamingSetting(boolean isEnabled) { + try { + getITelephony().setVoWiFiRoamingSetting(mSubId, isEnabled); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting. + * Typically used during the Voice over WiFi registration process for some carriers. + * + * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false + * otherwise. + * @param mode the Voice over WiFi mode preference to set, which can be one of the following: + * - {@link #WIFI_MODE_WIFI_ONLY} + * - {@link #WIFI_MODE_CELLULAR_PREFERRED} + * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @see #setVoWiFiSetting(boolean) + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setVoWiFiNonPersistent(boolean isCapable, int mode) { + try { + getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * @return The Voice over WiFi Mode preference set by the user, which can be one of the + * following: + * - {@link #WIFI_MODE_WIFI_ONLY} + * - {@link #WIFI_MODE_CELLULAR_PREFERRED} + * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @see #setVoWiFiSetting(boolean) + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @WiFiCallingMode int getVoWiFiModeSetting() { + try { + return getITelephony().getVoWiFiModeSetting(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Set the user's preference for Voice over WiFi calling mode. + * @param mode The user's preference for the technology to register for IMS over, can be one of + * the following: + * - {@link #WIFI_MODE_WIFI_ONLY} + * - {@link #WIFI_MODE_CELLULAR_PREFERRED} + * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @see #getVoWiFiModeSetting() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { + try { + getITelephony().setVoWiFiModeSetting(mSubId, mode); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Set the user's preference for Voice over WiFi calling mode while the device is roaming on + * another network. + * + * @return The user's preference for the technology to register for IMS over when roaming on + * another network, can be one of the following: + * - {@link #WIFI_MODE_WIFI_ONLY} + * - {@link #WIFI_MODE_CELLULAR_PREFERRED} + * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @see #setVoWiFiRoamingSetting(boolean) + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @WiFiCallingMode int getVoWiFiRoamingModeSetting() { + try { + return getITelephony().getVoWiFiRoamingModeSetting(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Set the user's preference for Voice over WiFi mode while the device is roaming on another + * network. + * + * @param mode The user's preference for the technology to register for IMS over when roaming on + * another network, can be one of the following: + * - {@link #WIFI_MODE_WIFI_ONLY} + * - {@link #WIFI_MODE_CELLULAR_PREFERRED} + * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @see #getVoWiFiRoamingModeSetting() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { + try { + getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Change the user's setting for RTT capability of this device. + * @param isEnabled if true RTT will be enabled during calls. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setRttCapabilitySetting(boolean isEnabled) { + try { + getITelephony().setRttCapabilitySetting(mSubId, isEnabled); + return; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * @return true if TTY over VoLTE is supported + * @see android.telecom.TelecomManager#getCurrentTtyMode + * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + boolean isTtyOverVolteEnabled() { + try { + return getITelephony().isTtyOverVolteEnabled(mSubId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + private static SubscriptionManager getSubscriptionManager(Context context) { + SubscriptionManager manager = context.getSystemService(SubscriptionManager.class); + if (manager == null) { + throw new RuntimeException("Could not find SubscriptionManager."); + } + return manager; + } + + private static ITelephony getITelephony() { + ITelephony binder = ITelephony.Stub.asInterface( + ServiceManager.getService(Context.TELEPHONY_SERVICE)); + if (binder == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + return binder; + } +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl index 4f37caa33680..749b1916962e 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl @@ -23,7 +23,7 @@ import android.telephony.ims.stub.ImsFeatureConfiguration; import android.telephony.ims.ImsReasonInfo; /** - * See ImsRegistrationImplBase.Callback for more information. + * See {@link ImsManager#RegistrationCallback} for more information. * * {@hide} */ diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index b77881e29f1e..7f69f43f6cea 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -167,59 +167,6 @@ public abstract class ImsFeature { */ public static final int CAPABILITY_SUCCESS = 0; - - /** - * The framework implements this callback in order to register for Feature Capability status - * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability - * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error - * callbacks when the ImsService can not change the capability as requested, via - * {@link #onChangeCapabilityConfigurationError}. - * - * @hide - */ - public static class CapabilityCallback extends IImsCapabilityCallback.Stub { - - @Override - public final void onCapabilitiesStatusChanged(int config) throws RemoteException { - onCapabilitiesStatusChanged(new Capabilities(config)); - } - - /** - * Returns the result of a query for the capability configuration of a requested capability. - * - * @param capability The capability that was requested. - * @param radioTech The IMS radio technology associated with the capability. - * @param isEnabled true if the capability is enabled, false otherwise. - */ - @Override - public void onQueryCapabilityConfiguration(int capability, int radioTech, - boolean isEnabled) { - - } - - /** - * Called when a change to the capability configuration has returned an error. - * - * @param capability The capability that was requested to be changed. - * @param radioTech The IMS radio technology associated with the capability. - * @param reason error associated with the failure to change configuration. - */ - @Override - public void onChangeCapabilityConfigurationError(int capability, int radioTech, - @ImsCapabilityError int reason) { - } - - /** - * The status of the feature's capabilities has changed to either available or unavailable. - * If unavailable, the feature is not able to support the unavailable capability at this - * time. - * - * @param config The new availability of the capabilities. - */ - public void onCapabilitiesStatusChanged(Capabilities config) { - } - } - /** * Used by the ImsFeature to call back to the CapabilityCallback that the framework has * provided. diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 7681aefc07dc..969959433f23 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -17,6 +17,8 @@ package android.telephony.ims.feature; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Bundle; import android.os.Message; @@ -222,21 +224,31 @@ public class MmTelFeature extends ImsFeature { * This MmTelFeature can then return the status of each of these capabilities (enabled or not) * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current * status can also be queried using {@link #queryCapabilityStatus()}. + * @see #isCapable(int) */ public static class MmTelCapabilities extends Capabilities { /** - * @hide + * Create a new empty {@link MmTelCapabilities} instance. + * @see #addCapabilities(int) + * @see #removeCapabilities(int) */ @VisibleForTesting public MmTelCapabilities() { super(); } + /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/ + @Deprecated public MmTelCapabilities(Capabilities c) { mCapabilities = c.mCapabilities; } + /** + * Create a new {link @MmTelCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities that are supported for MmTel in the form of a + * bitfield. + */ public MmTelCapabilities(int capabilities) { mCapabilities = capabilities; } @@ -406,7 +418,10 @@ public class MmTelFeature extends ImsFeature { * support the capability that is enabled. A capability that is disabled by the framework (via * {@link #changeEnabledCapabilities}) should also show the status as disabled. */ - public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) { + public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) { + if (c == null) { + throw new IllegalArgumentException("MmTelCapabilities must be non-null!"); + } super.notifyCapabilitiesStatusChanged(c); } @@ -414,7 +429,12 @@ public class MmTelFeature extends ImsFeature { * Notify the framework of an incoming call. * @param c The {@link ImsCallSessionImplBase} of the new incoming call. */ - public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) { + public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c, + @NonNull Bundle extras) { + if (c == null || extras == null) { + throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be " + + "null."); + } synchronized (mLock) { if (mListener == null) { throw new IllegalStateException("Session is not available."); @@ -434,7 +454,12 @@ public class MmTelFeature extends ImsFeature { * This can be null if no call information is available for the rejected call. * @param reason The {@link ImsReasonInfo} call rejection reason. */ - public final void notifyRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) { + public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile, + @NonNull ImsReasonInfo reason) { + if (callProfile == null || reason == null) { + throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be " + + "null."); + } synchronized (mLock) { if (mListener == null) { throw new IllegalStateException("Session is not available."); @@ -508,8 +533,8 @@ public class MmTelFeature extends ImsFeature { * the framework. */ @Override - public void changeEnabledCapabilities(CapabilityChangeRequest request, - CapabilityCallbackProxy c) { + public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, + @NonNull CapabilityCallbackProxy c) { // Base implementation, no-op } @@ -531,7 +556,7 @@ public class MmTelFeature extends ImsFeature { * {@link ImsCallProfile#CALL_TYPE_VS_RX} * @return a {@link ImsCallProfile} object */ - public ImsCallProfile createCallProfile(int callSessionType, int callType) { + public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) { // Base Implementation - Should be overridden return null; } @@ -552,7 +577,7 @@ public class MmTelFeature extends ImsFeature { * * @param profile a call profile to make the call */ - public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) { + public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) { // Base Implementation - Should be overridden return null; } @@ -569,7 +594,7 @@ public class MmTelFeature extends ImsFeature { * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the * call will be placed over IMS or via CSFB. */ - public @ProcessCallResult int shouldProcessCall(String[] numbers) { + public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) { return PROCESS_CALL_IMS; } @@ -602,7 +627,7 @@ public class MmTelFeature extends ImsFeature { * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service * configuration. */ - public ImsUtImplBase getUt() { + public @NonNull ImsUtImplBase getUt() { // Base Implementation - Should be overridden return new ImsUtImplBase(); } @@ -611,7 +636,7 @@ public class MmTelFeature extends ImsFeature { * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE * calls that support it. */ - public ImsEcbmImplBase getEcbm() { + public @NonNull ImsEcbmImplBase getEcbm() { // Base Implementation - Should be overridden return new ImsEcbmImplBase(); } @@ -620,7 +645,7 @@ public class MmTelFeature extends ImsFeature { * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event * package processing for multi-endpoint. */ - public ImsMultiEndpointImplBase getMultiEndpoint() { + public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() { // Base Implementation - Should be overridden return new ImsMultiEndpointImplBase(); } @@ -646,7 +671,7 @@ public class MmTelFeature extends ImsFeature { * } * } */ - public void setUiTtyMode(int mode, Message onCompleteMessage) { + public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) { // Base Implementation - Should be overridden } @@ -680,7 +705,7 @@ public class MmTelFeature extends ImsFeature { * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS * Provider. */ - public ImsSmsImplBase getSmsImplementation() { + public @NonNull ImsSmsImplBase getSmsImplementation() { return new ImsSmsImplBase(); } diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index cecf2e26f139..a08e0313bb5b 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -76,64 +76,6 @@ public class ImsRegistrationImplBase { private static final int REGISTRATION_STATE_REGISTERING = 1; private static final int REGISTRATION_STATE_REGISTERED = 2; - /** - * Callback class for receiving Registration callback events. - * @hide - */ - public static class Callback extends IImsRegistrationCallback.Stub { - /** - * Notifies the framework when the IMS Provider is connected to the IMS network. - * - * @param imsRadioTech the radio access technology. Valid values are defined in - * {@link ImsRegistrationTech}. - */ - @Override - public void onRegistered(@ImsRegistrationTech int imsRadioTech) { - } - - /** - * Notifies the framework when the IMS Provider is trying to connect the IMS network. - * - * @param imsRadioTech the radio access technology. Valid values are defined in - * {@link ImsRegistrationTech}. - */ - @Override - public void onRegistering(@ImsRegistrationTech int imsRadioTech) { - } - - /** - * Notifies the framework when the IMS Provider is disconnected from the IMS network. - * - * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. - */ - @Override - public void onDeregistered(ImsReasonInfo info) { - } - - /** - * A failure has occurred when trying to handover registration to another technology type, - * defined in {@link ImsRegistrationTech} - * - * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed - * @param info A {@link ImsReasonInfo} that identifies the reason for failure. - */ - @Override - public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, - ImsReasonInfo info) { - } - - /** - * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when - * it changes. - * @param uris new array of subscriber {@link Uri}s that are associated with this IMS - * subscription. - */ - @Override - public void onSubscriberAssociatedUriChanged(Uri[] uris) { - - } - } - private final IImsRegistration mBinder = new IImsRegistration.Stub() { @Override diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 86cb1b74abd3..b0c875e0c6f2 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -100,6 +100,7 @@ public class DctConstants { public static final int EVENT_DATA_RECONNECT = BASE + 47; public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48; public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49; + public static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = BASE + 50; /***** Constants *****/ diff --git a/telephony/java/com/android/internal/telephony/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/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 9730ebc57fcf..eda8e7766054 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -29,6 +29,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.provider.Settings; import android.telephony.Rlog; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -44,10 +45,6 @@ public final class TelephonyPermissions { private static final boolean DBG = false; - // When set to true this flag will treat all apps that fail the device identifier check as - // though they are targeting pre-Q and return dummy data instead of throwing a SecurityException - private static final boolean RELAX_DEVICE_IDENTIFIER_CHECK = true; - private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () -> ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); @@ -280,23 +277,29 @@ public final class TelephonyPermissions { */ private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid, int uid, String callingPackage, String message) { - // if the device identifier check is relaxed then just return false to return dummy data to - // the caller instead of throwing a SecurityException for apps targeting Q+. - if (RELAX_DEVICE_IDENTIFIER_CHECK) { - Log.wtf(LOG_TAG, - "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message); - return false; + Log.wtf(LOG_TAG, + "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message); + // if the device identifier check is relaxed then revert to the READ_PHONE_STATE permission + // check that was previously required to access device identifiers. + boolean relaxDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 0; + if (relaxDeviceIdentifierCheck) { + return checkReadPhoneState(context, subId, pid, uid, callingPackage, message); } else { + boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0; if (callingPackage != null) { try { - // if the target SDK is pre-Q then check if the calling package would have - // previously had access to device identifiers. + // if the target SDK is pre-Q or the target Q behavior is disabled then check if + // the calling package would have previously had access to device identifiers. ApplicationInfo callingPackageInfo = context.getPackageManager().getApplicationInfo( callingPackage, 0); - if (callingPackageInfo != null - && callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q) { - if (context.checkPermission(android.Manifest.permission.READ_PHONE_STATE, + if (callingPackageInfo != null && ( + callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q + || targetQBehaviorDisabled)) { + if (context.checkPermission( + android.Manifest.permission.READ_PHONE_STATE, pid, uid) == PackageManager.PERMISSION_GRANTED) { return false; @@ -312,8 +315,8 @@ public final class TelephonyPermissions { // default to throwing the SecurityException. } } - throw new SecurityException(message + ": The user " + uid + " does not have the " - + "READ_PRIVILEGED_PHONE_STATE permission to access the device identifiers"); + throw new SecurityException(message + ": The user " + uid + + " does not meet the requirements to access device identifiers."); } } diff --git a/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. */ diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py new file mode 100755 index 000000000000..48c07553ffef --- /dev/null +++ b/tools/hiddenapi/merge_csv.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Merge mutliple CSV files, possibly with different columns, writing to stdout. +""" + +import csv +import sys + +csv_readers = [ + csv.DictReader(open(csv_file, 'rb'), delimiter=',', quotechar='|') + for csv_file in sys.argv[1:] +] + +# Build union of all columns from source files: +headers = set() +for reader in csv_readers: + headers = headers.union(reader.fieldnames) + +# Concatenate all files to output: +out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', fieldnames = sorted(headers)) +out.writeheader() +for reader in csv_readers: + for row in reader: + out.writerow(row) + + |