Merge "Prevents uninstalled packages from being added to the DB."
diff --git a/Android.bp b/Android.bp
index f9b60e6..d94bd84 100644
--- a/Android.bp
+++ b/Android.bp
@@ -560,6 +560,7 @@
"telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
+ "telephony/java/com/android/internal/telephony/IRcs.aidl",
"telephony/java/com/android/internal/telephony/ISms.aidl",
"telephony/java/com/android/internal/telephony/ISub.aidl",
"telephony/java/com/android/internal/telephony/IAns.aidl",
@@ -1220,11 +1221,6 @@
srcs_lib_whitelist_dirs: frameworks_base_subdirs,
srcs_lib_whitelist_pkgs: packages_to_document,
libs: [
- "core-oj",
- "core-libart",
- "conscrypt",
- "bouncycastle",
- "okhttp",
"ext",
"framework",
"voip-common",
diff --git a/api/current.txt b/api/current.txt
index 5951433..1983fb4 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -1403,6 +1403,7 @@
field public static final int textFilterEnabled = 16843007; // 0x10100ff
field public static final int textFontWeight = 16844165; // 0x1010585
field public static final int textIsSelectable = 16843542; // 0x1010316
+ field public static final int textLocale = 16844178; // 0x1010592
field public static final int textOff = 16843045; // 0x1010125
field public static final int textOn = 16843044; // 0x1010124
field public static final int textScaleX = 16843089; // 0x1010151
@@ -9167,6 +9168,7 @@
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
method public android.net.Uri canonicalize(android.net.Uri);
+ method public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final java.lang.String getCallingPackage();
@@ -9194,6 +9196,7 @@
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
+ method public final void restoreCallingIdentity(android.content.ContentProvider.CallingIdentity);
method protected final void setPathPermissions(android.content.pm.PathPermission[]);
method protected final void setReadPermission(java.lang.String);
method protected final void setWritePermission(java.lang.String);
@@ -9202,6 +9205,9 @@
method public abstract int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
}
+ public final class ContentProvider.CallingIdentity {
+ }
+
public static abstract interface ContentProvider.PipeDataWriter<T> {
method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
}
@@ -9316,6 +9322,7 @@
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public static boolean isSyncActive(android.accounts.Account, java.lang.String);
method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
+ method public android.graphics.Bitmap loadThumbnail(android.net.Uri, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
@@ -15468,8 +15475,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
public static class MediaStore.Video.Thumbnails implements android.provider.BaseColumns {
ctor public MediaStore.Video.Thumbnails();
- method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
- method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+ method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+ method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
method public static android.net.Uri getContentUri(java.lang.String);
- method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
- method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
- field public static final java.lang.String DATA = "_data";
+ method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+ method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+ field public static final deprecated java.lang.String DATA = "_data";
field public static final java.lang.String DEFAULT_SORT_ORDER = "video_id ASC";
field public static final android.net.Uri EXTERNAL_CONTENT_URI;
field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -45199,6 +45272,7 @@
method public int getSpanTypeId();
method public android.content.res.ColorStateList getTextColor();
method public int getTextFontWeight();
+ method public android.os.LocaleList getTextLocales();
method public int getTextSize();
method public int getTextStyle();
method public android.graphics.Typeface getTypeface();
@@ -51350,6 +51424,7 @@
method public default android.view.textclassifier.TextClassification classifyText(android.view.textclassifier.TextClassification.Request);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default void destroy();
+ method public default android.view.textclassifier.TextLanguage detectLanguage(android.view.textclassifier.TextLanguage.Request);
method public default android.view.textclassifier.TextLinks generateLinks(android.view.textclassifier.TextLinks.Request);
method public default int getMaxGenerateLinksTextLength();
method public default boolean isDestroyed();
@@ -51390,6 +51465,39 @@
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
}
+ public final class TextLanguage implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getConfidenceScore(android.icu.util.ULocale);
+ method public android.os.Bundle getExtras();
+ method public java.lang.String getId();
+ method public android.icu.util.ULocale getLocale(int);
+ method public int getLocaleHypothesisCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage> CREATOR;
+ }
+
+ public static final class TextLanguage.Builder {
+ ctor public TextLanguage.Builder();
+ method public android.view.textclassifier.TextLanguage build();
+ method public android.view.textclassifier.TextLanguage.Builder putLocale(android.icu.util.ULocale, float);
+ method public android.view.textclassifier.TextLanguage.Builder setExtras(android.os.Bundle);
+ method public android.view.textclassifier.TextLanguage.Builder setId(java.lang.String);
+ }
+
+ public static final class TextLanguage.Request implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getExtras();
+ method public java.lang.CharSequence getText();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage.Request> CREATOR;
+ }
+
+ public static final class TextLanguage.Request.Builder {
+ ctor public TextLanguage.Request.Builder(java.lang.CharSequence);
+ method public android.view.textclassifier.TextLanguage.Request build();
+ method public android.view.textclassifier.TextLanguage.Request.Builder setExtras(android.os.Bundle);
+ }
+
public final class TextLinks implements android.os.Parcelable {
method public int apply(android.text.Spannable, int, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan>);
method public int describeContents();
diff --git a/api/system-current.txt b/api/system-current.txt
index 580a760..26036ea 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4471,6 +4471,23 @@
field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
}
+ public static final class Telephony.Carriers implements android.provider.BaseColumns {
+ field public static final java.lang.String APN_SET_ID = "apn_set_id";
+ field public static final int CARRIER_EDITED = 4; // 0x4
+ field public static final java.lang.String EDITED = "edited";
+ field public static final java.lang.String MAX_CONNS = "max_conns";
+ field public static final java.lang.String MAX_CONNS_TIME = "max_conns_time";
+ field public static final java.lang.String MODEM_COGNITIVE = "modem_cognitive";
+ field public static final java.lang.String MTU = "mtu";
+ field public static final int NO_SET_SET = 0; // 0x0
+ field public static final int UNEDITED = 0; // 0x0
+ field public static final int USER_DELETED = 2; // 0x2
+ field public static final java.lang.String USER_EDITABLE = "user_editable";
+ field public static final int USER_EDITED = 1; // 0x1
+ field public static final java.lang.String USER_VISIBLE = "user_visible";
+ field public static final java.lang.String WAIT_TIME = "wait_time";
+ }
+
public final class TimeZoneRulesDataContract {
field public static final java.lang.String AUTHORITY = "com.android.timezone";
}
@@ -5486,6 +5503,7 @@
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+ method public void setCarrierDataEnabled(boolean);
method public void setDataActivationState(int);
method public deprecated void setDataEnabled(int, boolean);
method public void setDataRoamingEnabled(boolean);
@@ -6372,7 +6390,8 @@
}
public static class MmTelFeature.MmTelCapabilities {
- ctor public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+ ctor public MmTelFeature.MmTelCapabilities();
+ ctor public deprecated MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
ctor public MmTelFeature.MmTelCapabilities(int);
method public final void addCapabilities(int);
method public final boolean isCapable(int);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 10b7c0b..39d24ee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -143,6 +143,7 @@
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 @@
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 @@
// 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/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 857c390..bbc3f35 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1383,62 +1383,6 @@
Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V
Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
-Landroid/system/Int32Ref;->value:I
-Landroid/system/OsConstants;-><init>()V
-Landroid/system/OsConstants;->AF_NETLINK:I
-Landroid/system/OsConstants;->AF_PACKET:I
-Landroid/system/OsConstants;->ARPHRD_ETHER:I
-Landroid/system/OsConstants;->ARPHRD_LOOPBACK:I
-Landroid/system/OsConstants;->CAP_TO_INDEX(I)I
-Landroid/system/OsConstants;->CAP_TO_MASK(I)I
-Landroid/system/OsConstants;->ENONET:I
-Landroid/system/OsConstants;->ETH_P_ALL:I
-Landroid/system/OsConstants;->ETH_P_ARP:I
-Landroid/system/OsConstants;->ETH_P_IP:I
-Landroid/system/OsConstants;->ETH_P_IPV6:I
-Landroid/system/OsConstants;->EUSERS:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REPLY:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REQUEST:I
-Landroid/system/OsConstants;->ICMP_ECHO:I
-Landroid/system/OsConstants;->ICMP_ECHOREPLY:I
-Landroid/system/OsConstants;->initConstants()V
-Landroid/system/OsConstants;->IP_MULTICAST_ALL:I
-Landroid/system/OsConstants;->IP_RECVTOS:I
-Landroid/system/OsConstants;->MAP_POPULATE:I
-Landroid/system/OsConstants;->NETLINK_NETFILTER:I
-Landroid/system/OsConstants;->NETLINK_ROUTE:I
-Landroid/system/OsConstants;->O_DIRECT:I
-Landroid/system/OsConstants;->placeholder()I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT:I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT_RAISE:I
-Landroid/system/OsConstants;->RLIMIT_NOFILE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_RULE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFINFO:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_PREFIX:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_LINK:I
-Landroid/system/OsConstants;->RTMGRP_NEIGH:I
-Landroid/system/OsConstants;->RTMGRP_NOTIFY:I
-Landroid/system/OsConstants;->RTMGRP_TC:I
-Landroid/system/OsConstants;->SO_DOMAIN:I
-Landroid/system/OsConstants;->SO_PROTOCOL:I
-Landroid/system/OsConstants;->SPLICE_F_MORE:I
-Landroid/system/OsConstants;->SPLICE_F_MOVE:I
-Landroid/system/OsConstants;->SPLICE_F_NONBLOCK:I
-Landroid/system/OsConstants;->TIOCOUTQ:I
-Landroid/system/OsConstants;->UDP_ENCAP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP_NON_IKE:I
-Landroid/system/OsConstants;->UNIX_PATH_MAX:I
-Landroid/system/OsConstants;->XATTR_CREATE:I
-Landroid/system/OsConstants;->XATTR_REPLACE:I
-Landroid/system/OsConstants;->_LINUX_CAPABILITY_VERSION_3:I
-Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telephony/TelephonyManager$MultiSimVariants;
Landroid/util/Singleton;-><init>()V
@@ -2339,93 +2283,6 @@
Lcom/google/android/mms/util/PduCache;->purgeAll()V
Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
-Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
-Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;-><init>(IILjava/lang/String;)V
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mMessage:Ljava/lang/String;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyState:I
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyViolated:I
-Ldalvik/system/BlockGuard$Policy;->onNetwork()V
-Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V
-Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->LAX_POLICY:Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->setThreadPolicy(Ldalvik/system/BlockGuard$Policy;)V
-Ldalvik/system/BlockGuard;->threadPolicy:Ljava/lang/ThreadLocal;
-Ldalvik/system/CloseGuard$DefaultReporter;-><init>()V
-Ldalvik/system/CloseGuard$Reporter;->report(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ldalvik/system/CloseGuard;-><init>()V
-Ldalvik/system/CloseGuard;->close()V
-Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
-Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
-Ldalvik/system/CloseGuard;->setEnabled(Z)V
-Ldalvik/system/CloseGuard;->setReporter(Ldalvik/system/CloseGuard$Reporter;)V
-Ldalvik/system/CloseGuard;->warnIfOpen()V
-Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String;
-Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
-Ldalvik/system/DexFile;->isBackedByOatFile()Z
-Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
-Ldalvik/system/DexFile;->loadDex(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
-Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFileNative(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V
-Ldalvik/system/DexPathList$Element;-><init>(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V
-Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList$Element;->path:Ljava/io/File;
-Ldalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V
-Ldalvik/system/DexPathList$NativeLibraryElement;->path:Ljava/io/File;
-Ldalvik/system/DexPathList;-><init>(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addNativePath(Ljava/util/Collection;)V
-Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader;
-Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->dexElementsSuppressedExceptions:[Ljava/io/IOException;
-Ldalvik/system/DexPathList;->loadDexFile(Ljava/io/File;Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makeInMemoryDexElements([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->splitPaths(Ljava/lang/String;Z)Ljava/util/List;
-Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/SocketTagger;->get()Ldalvik/system/SocketTagger;
-Ldalvik/system/SocketTagger;->tag(Ljava/net/Socket;)V
-Ldalvik/system/SocketTagger;->untag(Ljava/net/Socket;)V
-Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V
-Ldalvik/system/VMDebug;->dumpReferenceTables()V
-Ldalvik/system/VMDebug;->isDebuggerConnected()Z
-Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
-Ldalvik/system/VMRuntime;->clearGrowthLimit()V
-Ldalvik/system/VMRuntime;->gcSoftReferences()V
-Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getExternalBytesAllocated()J
-Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getMinimumHeapSize()J
-Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
-Ldalvik/system/VMRuntime;->is64Bit()Z
-Ldalvik/system/VMRuntime;->is64BitAbi(Ljava/lang/String;)Z
-Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
-Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
-Ldalvik/system/VMRuntime;->registerNativeFree(I)V
-Ldalvik/system/VMRuntime;->runFinalization(J)V
-Ldalvik/system/VMRuntime;->runFinalizationSync()V
-Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
-Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
-Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
-Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
-Ldalvik/system/VMRuntime;->trackExternalFree(J)V
-Ldalvik/system/VMRuntime;->vmInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->vmLibrary()Ljava/lang/String;
-Ldalvik/system/VMStack;->fillStackTraceElements(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I
-Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
-Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
-Ldalvik/system/VMStack;->getThreadStackTrace(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;
Ljava/io/Console;->encoding()Ljava/lang/String;
Ljava/io/File;->filePath:Ljava/nio/file/Path;
Ljava/io/File;->fs:Ljava/io/FileSystem;
@@ -2501,18 +2358,6 @@
Ljava/lang/Class;->objectSize:I
Ljava/lang/Class;->status:I
Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
-Ljava/lang/Daemons$Daemon;->isRunning()Z
-Ljava/lang/Daemons$Daemon;->start()V
-Ljava/lang/Daemons$Daemon;->stop()V
-Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
-Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
-Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
-Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
-Ljava/lang/Daemons$ReferenceQueueDaemon;->INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon;
-Ljava/lang/Daemons;->MAX_FINALIZE_NANOS:J
-Ljava/lang/Daemons;->requestHeapTrim()V
-Ljava/lang/Daemons;->start()V
-Ljava/lang/Daemons;->stop()V
Ljava/lang/Double;->value:D
Ljava/lang/Enum;->getSharedConstants(Ljava/lang/Class;)[Ljava/lang/Enum;
Ljava/lang/Enum;->name:Ljava/lang/String;
@@ -2522,11 +2367,6 @@
Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V
Ljava/lang/Long;->value:J
Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
-Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
-Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->queue:Ljava/lang/ref/ReferenceQueue;
-Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V
Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
@@ -2669,12 +2509,6 @@
Ljava/nio/charset/Charset;->defaultCharset:Ljava/nio/charset/Charset;
Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
-Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
-Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
-Ljava/nio/NioUtils;->freeDirectBuffer(Ljava/nio/ByteBuffer;)V
-Ljava/nio/NioUtils;->unsafeArray(Ljava/nio/ByteBuffer;)[B
-Ljava/nio/NioUtils;->unsafeArrayOffset(Ljava/nio/ByteBuffer;)I
Ljava/security/KeyPairGenerator;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/KeyPairGenerator;
Ljava/security/KeyStore;->keyStoreSpi:Ljava/security/KeyStoreSpi;
Ljava/security/Signature;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/Signature;
@@ -2790,9 +2624,6 @@
Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
-Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/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;->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 45e38cf9..e5e64d3 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -122,14 +122,6 @@
Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
Landroid/os/UserManager;->isAdminUser()Z
Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/system/NetlinkSocketAddress;-><init>(II)V
-Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->sendto(Ljava/io/FileDescriptor;[BIIILjava/net/SocketAddress;)I
-Landroid/system/Os;->setsockoptIfreq(Ljava/io/FileDescriptor;IILjava/lang/String;)V
-Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Landroid/system/PacketSocketAddress;-><init>(I[B)V
-Landroid/system/PacketSocketAddress;-><init>(SI)V
Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
Landroid/telephony/ims/compat/ImsService;-><init>()V
Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 56ca98f..1a8a32e 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2354,6 +2354,7 @@
android.nfc.NfcAdapter$CreateNdefMessageCallback
android.nfc.NfcManager
android.opengl.EGL14
+android.opengl.EGL15
android.opengl.EGLConfig
android.opengl.EGLContext
android.opengl.EGLDisplay
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d00650d..069effd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -250,4 +250,34 @@
public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
boolean aboveSystem, String reason);
+
+ /**
+ * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
+ * flags.
+ */
+ public abstract void broadcastGlobalConfigurationChanged(int changes, boolean initLocale);
+
+ /**
+ * Sends {@link android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} with all the appropriate
+ * flags.
+ */
+ public abstract void broadcastCloseSystemDialogs(String reason);
+
+ /**
+ * Kills all background processes, except those matching any of the specified properties.
+ *
+ * @param minTargetSdk the target SDK version at or above which to preserve processes,
+ * or {@code -1} to ignore the target SDK
+ * @param maxProcState the process state at or below which to preserve processes,
+ * or {@code -1} to ignore the process state
+ */
+ public abstract void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState);
+
+ /** Starts a given process. */
+ public abstract void startProcess(String processName, ApplicationInfo info,
+ boolean knownToBeDead, String hostingType, ComponentName hostingName);
+
+ /** Starts up the starting activity process for debugging if needed. */
+ public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+ ProfilerInfo profilerInfo);
}
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index ceb828b..0e428ae 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -1013,6 +1013,11 @@
replaceTargets(sharedElementTransition, sharedElementsIn, null);
}
}
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ }
});
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b289a3e..92daf08 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -51,6 +51,7 @@
import android.graphics.Bitmap;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -5786,7 +5787,8 @@
}
if (mService != null) {
try {
- return mService.checkDeviceIdentifierAccess(packageName, userId);
+ return mService.checkDeviceIdentifierAccess(packageName, userId,
+ Binder.getCallingPid(), Binder.getCallingUid());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cf0cad8..ce1f4ef 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -153,7 +153,7 @@
void clearProfileOwner(in ComponentName who);
boolean hasUserSetupCompleted();
- boolean checkDeviceIdentifierAccess(in String packageName, int userHandle);
+ boolean checkDeviceIdentifierAccess(in String packageName, int userHandle, int pid, int uid);
void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
CharSequence getDeviceOwnerLockScreenInfo();
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index fda2f89..cb996f3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -183,7 +184,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index e7c8944..c447868 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -23,6 +23,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -138,7 +139,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 9b7c173..d616b8f 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -57,6 +57,7 @@
private int mAuthRetryState;
private int mConnState;
private final Object mStateLock = new Object();
+ private final Object mDeviceBusyLock = new Object();
@UnsupportedAppUsage
private Boolean mDeviceBusy = false;
@UnsupportedAppUsage
@@ -282,7 +283,7 @@
}
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
}
@@ -357,7 +358,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -413,7 +414,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -496,7 +497,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -547,7 +548,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -596,7 +597,7 @@
return;
}
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
@@ -1098,7 +1099,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1132,7 +1133,7 @@
if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
if (mService == null || mClientIf == 0) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1178,7 +1179,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1221,7 +1222,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1262,7 +1263,7 @@
BluetoothDevice device = service.getDevice();
if (device == null) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
@@ -1330,7 +1331,7 @@
if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
- synchronized (mDeviceBusy) {
+ synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ec18d42..549c1fa 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -428,7 +429,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index b967fb2..22d41d9 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -491,7 +492,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 606f00a..47c4ee6 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -29,6 +29,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -205,7 +206,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- android.os.Process.myUserHandle())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
return;
}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 3bc8544..e44f36e 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -24,6 +24,7 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -454,7 +455,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 0ca39f1..58a2522 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -25,6 +25,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -279,7 +280,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 98c23c6..fc5f830 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -110,7 +111,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 559a59b..1c82e19 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -25,6 +25,7 @@
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -128,7 +129,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 58be732..8923d73 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -26,6 +26,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -150,7 +151,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index ae264e1..a601df0 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -24,6 +24,7 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -164,7 +165,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 1446adc..cbc96c0 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -23,6 +23,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -116,7 +117,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 1b73206..ebf6bed 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -148,7 +149,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- mContext.getUser())) {
+ UserHandle.CURRENT_OR_SELF)) {
Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
return false;
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 2a03787..145c9273 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -822,6 +822,48 @@
}
/**
+ * Opaque token representing the identity of an incoming IPC.
+ */
+ public final class CallingIdentity {
+ /** {@hide} */
+ public final long binderToken;
+ /** {@hide} */
+ public final String callingPackage;
+
+ /** {@hide} */
+ public CallingIdentity(long binderToken, String callingPackage) {
+ this.binderToken = binderToken;
+ this.callingPackage = callingPackage;
+ }
+ }
+
+ /**
+ * Reset the identity of the incoming IPC on the current thread.
+ * <p>
+ * Internally this calls {@link Binder#clearCallingIdentity()} and also
+ * clears any value stored in {@link #getCallingPackage()}.
+ *
+ * @return Returns an opaque token that can be used to restore the original
+ * calling identity by passing it to
+ * {@link #restoreCallingIdentity}.
+ */
+ public final @NonNull CallingIdentity clearCallingIdentity() {
+ return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+ }
+
+ /**
+ * Restore the identity of the incoming IPC on the current thread back to a
+ * previously identity that was returned by {@link #clearCallingIdentity}.
+ * <p>
+ * Internally this calls {@link Binder#restoreCallingIdentity(long)} and
+ * also restores any value stored in {@link #getCallingPackage()}.
+ */
+ public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
+ Binder.restoreCallingIdentity(identity.binderToken);
+ mCallingPackage.set(identity.callingPackage);
+ }
+
+ /**
* Change the authorities of the ContentProvider.
* This is normally set for you from its manifest information when the provider is first
* created.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c19909d..599c2d2 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -35,6 +35,12 @@
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.database.IContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -49,9 +55,11 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DocumentsContract;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.util.Size;
import com.android.internal.util.MimeIconUtils;
import com.android.internal.util.Preconditions;
@@ -68,6 +76,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -3188,4 +3197,62 @@
}
return query;
}
+
+ /**
+ * Convenience method that efficiently loads a visual thumbnail for the
+ * given {@link Uri}. Internally calls
+ * {@link ContentProvider#openTypedAssetFile} on the remote provider, but
+ * also defensively resizes any returned content to match the requested
+ * target size.
+ *
+ * @param uri The item that should be visualized as a thumbnail.
+ * @param size The target area on the screen where this thumbnail will be
+ * shown. This is passed to the provider as {@link #EXTRA_SIZE}
+ * to help it avoid downloading or generating heavy resources.
+ * @param signal A signal to cancel the operation in progress.
+ * @return Valid {@link Bitmap} which is a visual thumbnail.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
+ */
+ public @NonNull Bitmap loadThumbnail(@NonNull Uri uri, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ Objects.requireNonNull(uri);
+ Objects.requireNonNull(size);
+
+ try (ContentProviderClient client = acquireContentProviderClient(uri)) {
+ return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_DEFAULT);
+ }
+ }
+
+ /** {@hide} */
+ public static Bitmap loadThumbnail(@NonNull ContentProviderClient client, @NonNull Uri uri,
+ @NonNull Size size, @Nullable CancellationSignal signal, int allocator)
+ throws IOException {
+ Objects.requireNonNull(client);
+ Objects.requireNonNull(uri);
+ Objects.requireNonNull(size);
+
+ // Convert to Point, since that's what the API is defined as
+ final Bundle opts = new Bundle();
+ opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+ return client.openTypedAssetFileDescriptor(uri, "image/*", opts, signal);
+ }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
+ decoder.setAllocator(allocator);
+
+ // One last-ditch check to see if we've been canceled.
+ if (signal != null) signal.throwIfCanceled();
+
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final int widthSample = info.getSize().getWidth() / size.getWidth();
+ final int heightSample = info.getSize().getHeight() / size.getHeight();
+ final int sample = Math.min(widthSample, heightSample);
+ if (sample > 1) {
+ decoder.setTargetSampleSize(sample);
+ }
+ });
+ }
}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 6609b76..5de89e3 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -105,12 +105,15 @@
*
* This method should be used if the caller wants to receive notifications even after the
* process exits. The client must have an open connection with the Context Hub Service (i.e. it
- * cannot have been closed through the {@link #close()} method). If registered successfully,
- * intents will be delivered regarding events for the specified nanoapp from the attached
- * Context Hub. Any unicast messages for this client will also be delivered. The intent will
- * have an extra {@link #EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which will
- * contain the type of the event. See {@link ContextHubManager.Event} for description of each
- * event type.
+ * cannot have been closed through the {@link #close()} method). Only one PendingIntent can be
+ * registered at a time for a single ContextHubClient. If registered successfully, intents will
+ * be delivered regarding events for the specified nanoapp from the attached Context Hub. Any
+ * unicast messages for this client will also be delivered. The intent will have an extra
+ * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
+ * describes the Context Hub the intent event was for. The intent will also have an extra
+ * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
+ * will contain the type of the event. See {@link ContextHubManager.Event} for description of
+ * each event type, along with event-specific extra fields.
*
* When the intent is received, this client can be recreated through
* {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1fbfa40..4714587 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2816,10 +2816,11 @@
* @param network The {@link Network} of the satisfying network.
* @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
* @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide
*/
public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
- LinkProperties linkProperties) {
+ LinkProperties linkProperties, boolean blocked) {
// Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior.
@@ -2830,6 +2831,7 @@
}
onCapabilitiesChanged(network, networkCapabilities);
onLinkPropertiesChanged(network, linkProperties);
+ onBlockedStatusChanged(network, blocked);
}
/**
@@ -2837,7 +2839,8 @@
* This callback may be called more than once if the {@link Network} that is
* satisfying the request changes. This will always immediately be followed by a
* call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
- * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+ * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
+ * {@link #onBlockedStatusChanged(Network, boolean)}.
*
* @param network The {@link Network} of the satisfying network.
*/
@@ -2916,6 +2919,14 @@
*/
public void onNetworkResumed(Network network) {}
+ /**
+ * Called when access to the specified network is blocked or unblocked.
+ *
+ * @param network The {@link Network} whose blocked status has changed.
+ * @param blocked The blocked status of this {@link Network}.
+ */
+ public void onBlockedStatusChanged(Network network, boolean blocked) {}
+
private NetworkRequest networkRequest;
}
@@ -2962,6 +2973,8 @@
public static final int CALLBACK_SUSPENDED = BASE + 9;
/** @hide */
public static final int CALLBACK_RESUMED = BASE + 10;
+ /** @hide */
+ public static final int CALLBACK_BLK_CHANGED = BASE + 11;
/** @hide */
public static String getCallbackName(int whichCallback) {
@@ -2976,6 +2989,7 @@
case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED";
case CALLBACK_RESUMED: return "CALLBACK_RESUMED";
+ case CALLBACK_BLK_CHANGED: return "CALLBACK_BLK_CHANGED";
default:
return Integer.toString(whichCallback);
}
@@ -3022,7 +3036,7 @@
case CALLBACK_AVAILABLE: {
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
LinkProperties lp = getObject(message, LinkProperties.class);
- callback.onAvailable(network, cap, lp);
+ callback.onAvailable(network, cap, lp, message.arg1 != 0);
break;
}
case CALLBACK_LOSING: {
@@ -3055,6 +3069,10 @@
callback.onNetworkResumed(network);
break;
}
+ case CALLBACK_BLK_CHANGED: {
+ boolean blocked = message.arg1 != 0;
+ callback.onBlockedStatusChanged(network, blocked);
+ }
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 12b6f9e..0bdfca7 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1590,4 +1590,14 @@
Preconditions.checkArgument(isValidCapability(capability),
"NetworkCapability " + capability + "out of range");
}
+
+ /**
+ * Check if this {@code NetworkCapability} instance is metered.
+ *
+ * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+ * @hide
+ */
+ public boolean isMetered() {
+ return !hasCapability(NET_CAPABILITY_NOT_METERED);
+ }
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index d912dd10..1a1d2d334 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -202,7 +202,9 @@
* Return a network-type-specific integer describing the subtype
* of the network.
* @return the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
*/
+ @Deprecated
public int getSubtype() {
synchronized (this) {
return mSubtype;
@@ -243,7 +245,9 @@
/**
* Return a human-readable name describing the subtype of the network.
* @return the name of the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
*/
+ @Deprecated
public String getSubtypeName() {
synchronized (this) {
return mSubtypeName;
@@ -278,7 +282,15 @@
* connections and pass data.
* <p>Always call this before attempting to perform data transactions.
* @return {@code true} if network connectivity exists, {@code false} otherwise.
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
*/
+ @Deprecated
public boolean isConnected() {
synchronized (this) {
return mState == State.CONNECTED;
@@ -411,7 +423,15 @@
/**
* Reports the current fine-grained state of the network.
* @return the fine-grained state
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
*/
+ @Deprecated
public DetailedState getDetailedState() {
synchronized (this) {
return mDetailedState;
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 40465ce..d09f33b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1102,19 +1102,18 @@
public String getHost() {
@SuppressWarnings("StringEquality")
boolean cached = (host != NOT_CACHED);
- return cached ? host
- : (host = parseHost());
+ return cached ? host : (host = parseHost());
}
private String parseHost() {
- String authority = getEncodedAuthority();
+ final String authority = getEncodedAuthority();
if (authority == null) {
return null;
}
// Parse out user info and then port.
int userInfoSeparator = authority.lastIndexOf('@');
- int portSeparator = authority.indexOf(':', userInfoSeparator);
+ int portSeparator = findPortSeparator(authority);
String encodedHost = portSeparator == NOT_FOUND
? authority.substring(userInfoSeparator + 1)
@@ -1132,16 +1131,8 @@
}
private int parsePort() {
- String authority = getEncodedAuthority();
- if (authority == null) {
- return -1;
- }
-
- // Make sure we look for the port separtor *after* the user info
- // separator. We have URLs with a ':' in the user info.
- int userInfoSeparator = authority.lastIndexOf('@');
- int portSeparator = authority.indexOf(':', userInfoSeparator);
-
+ final String authority = getEncodedAuthority();
+ int portSeparator = findPortSeparator(authority);
if (portSeparator == NOT_FOUND) {
return -1;
}
@@ -1154,6 +1145,24 @@
return -1;
}
}
+
+ private int findPortSeparator(String authority) {
+ if (authority == null) {
+ return NOT_FOUND;
+ }
+
+ // Reverse search for the ':' character that breaks as soon as a char that is neither
+ // a colon nor an ascii digit is encountered. Thanks to the goodness of UTF-16 encoding,
+ // it's not possible that a surrogate matches one of these, so this loop can just
+ // look for characters rather than care about code points.
+ for (int i = authority.length() - 1; i >= 0; --i) {
+ final int character = authority.charAt(i);
+ if (':' == character) return i;
+ // Character.isDigit would include non-ascii digits
+ if (character < '0' || character > '9') return NOT_FOUND;
+ }
+ return NOT_FOUND;
+ }
}
/**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 412a700..292543c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -130,9 +130,9 @@
* <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain
* proof of the device's original identifiers.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. Profile owner access is
+ * deprecated and will be removed in a future release.
*
* @return The serial number if specified.
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0d5d547..00b989e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -272,6 +272,10 @@
*
* Specifies if all users on the device are disallowed from enabling the
* "Unknown Sources" setting, that allows installation of apps from unknown sources.
+ *
+ * This restriction can be enabled by the profile owner, in which case all accounts and
+ * profiles will be affected.
+ *
* The default value is <code>false</code>.
*
* <p>Key for user restrictions.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 50ca4ab..df771df 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1538,6 +1538,9 @@
* @hide
*/
public File translateAppToSystem(File file, String packageName) {
+ // We can only translate absolute paths
+ if (!file.isAbsolute()) return file;
+
try {
return new File(mStorageManager.translateAppToSystem(file.getAbsolutePath(),
packageName, mContext.getUserId()));
@@ -1553,6 +1556,9 @@
* @hide
*/
public File translateSystemToApp(File file, String packageName) {
+ // We can only translate absolute paths
+ if (!file.isAbsolute()) return file;
+
try {
return new File(mStorageManager.translateSystemToApp(file.getAbsolutePath(),
packageName, mContext.getUserId()));
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8c40e0e..954d18a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -34,6 +34,7 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.graphics.Point;
import android.media.ExifInterface;
@@ -53,6 +54,7 @@
import android.system.Os;
import android.util.DataUnit;
import android.util.Log;
+import android.util.Size;
import libcore.io.IoUtils;
@@ -136,10 +138,11 @@
public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
/**
- * Included in {@link AssetFileDescriptor#getExtras()} when returned
- * thumbnail should be rotated.
+ * An extra number of degrees that an image should be rotated during the
+ * decode process to be presented correctly.
*
- * @see MediaStore.Images.ImageColumns#ORIENTATION
+ * @see AssetFileDescriptor#getExtras()
+ * @see android.provider.MediaStore.Images.ImageColumns#ORIENTATION
*/
public static final String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
@@ -1093,75 +1096,10 @@
/** {@hide} */
@UnsupportedAppUsage
- public static Bitmap getDocumentThumbnail(
- ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
- throws RemoteException, IOException {
- final Bundle openOpts = new Bundle();
- openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size);
-
- AssetFileDescriptor afd = null;
- Bitmap bitmap = null;
- try {
- afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
-
- final FileDescriptor fd = afd.getFileDescriptor();
- final long offset = afd.getStartOffset();
-
- // Try seeking on the returned FD, since it gives us the most
- // optimal decode path; otherwise fall back to buffering.
- BufferedInputStream is = null;
- try {
- Os.lseek(fd, offset, SEEK_SET);
- } catch (ErrnoException e) {
- is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE);
- is.mark(THUMBNAIL_BUFFER_SIZE);
- }
-
- // We requested a rough thumbnail size, but the remote size may have
- // returned something giant, so defensively scale down as needed.
- final BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inJustDecodeBounds = true;
- if (is != null) {
- BitmapFactory.decodeStream(is, null, opts);
- } else {
- BitmapFactory.decodeFileDescriptor(fd, null, opts);
- }
-
- final int widthSample = opts.outWidth / size.x;
- final int heightSample = opts.outHeight / size.y;
-
- opts.inJustDecodeBounds = false;
- opts.inSampleSize = Math.min(widthSample, heightSample);
- if (is != null) {
- is.reset();
- bitmap = BitmapFactory.decodeStream(is, null, opts);
- } else {
- try {
- Os.lseek(fd, offset, SEEK_SET);
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
- }
- bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts);
- }
-
- // Transform the bitmap if requested. We use a side-channel to
- // communicate the orientation, since EXIF thumbnails don't contain
- // the rotation flags of the original image.
- final Bundle extras = afd.getExtras();
- final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
- if (orientation != 0) {
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
-
- final Matrix m = new Matrix();
- m.setRotate(orientation, width / 2, height / 2);
- bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
- }
- } finally {
- IoUtils.closeQuietly(afd);
- }
-
- return bitmap;
+ public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
+ Point size, CancellationSignal signal) throws IOException {
+ return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
+ ImageDecoder.ALLOCATOR_DEFAULT);
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 828fd73..f5660b9 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -29,7 +29,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
-import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Bitmap;
@@ -39,6 +38,7 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
+import android.os.OperationCanceledException;
import android.os.RemoteException;
import android.service.media.CameraPrewarmService;
import android.util.ArrayMap;
@@ -402,7 +402,16 @@
* access.
* <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access. This value will always be {@code NULL}
+ * for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
@@ -641,6 +650,7 @@
* This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
* to be accessed elsewhere.
*/
+ @Deprecated
private static class InternalThumbnails implements BaseColumns {
/**
* Currently outstanding thumbnail requests that can be cancelled.
@@ -654,13 +664,14 @@
*
* @see #cancelThumbnail(ContentResolver, Uri)
*/
+ @Deprecated
static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
int kind, @Nullable BitmapFactory.Options opts) {
- final Bundle openOpts = new Bundle();
+ final Point size;
if (kind == ThumbnailConstants.MICRO_KIND) {
- openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MICRO_SIZE);
+ size = ThumbnailConstants.MICRO_SIZE;
} else if (kind == ThumbnailConstants.MINI_KIND) {
- openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MINI_SIZE);
+ size = ThumbnailConstants.MINI_SIZE;
} else {
throw new IllegalArgumentException("Unsupported kind: " + kind);
}
@@ -674,9 +685,8 @@
}
}
- try (AssetFileDescriptor afd = cr.openTypedAssetFileDescriptor(uri,
- "image/*", openOpts, signal)) {
- return BitmapFactory.decodeFileDescriptor(afd.getFileDescriptor(), null, opts);
+ try {
+ return cr.loadThumbnail(uri, Point.convert(size), signal);
} catch (IOException e) {
Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
return null;
@@ -693,6 +703,7 @@
* Only the original process which made the request can cancel their own
* requests.
*/
+ @Deprecated
static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
synchronized (sPending) {
final CancellationSignal signal = sPending.get(uri);
@@ -936,9 +947,8 @@
}
/**
- * This class allows developers to query and get two kinds of thumbnails:
- * MINI_KIND: 512 x 384 thumbnail
- * MICRO_KIND: 96 x 96 thumbnail
+ * This class provides utility methods to obtain thumbnails for various
+ * {@link Images} items.
*/
public static class Thumbnails implements BaseColumns {
public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
@@ -958,13 +968,19 @@
}
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver
- * @param origId original image id
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
+ @Deprecated
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
final Uri uri = ContentUris.withAppendedId(
Images.Media.EXTERNAL_CONTENT_URI, origId);
@@ -972,51 +988,66 @@
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Return thumbnail representing a specific image item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image
- * associated with origId doesn't exist or memory is not enough.
+ * @param imageId the image item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long imageId, int kind,
BitmapFactory.Options options) {
final Uri uri = ContentUris.withAppendedId(
- Images.Media.EXTERNAL_CONTENT_URI, origId);
+ Images.Media.EXTERNAL_CONTENT_URI, imageId);
return InternalThumbnails.getThumbnail(cr, uri, kind, options);
}
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver
- * @param origId original image id
- * @param groupId the same groupId used in getThumbnail.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
+ @Deprecated
+ public static void cancelThumbnailRequest(ContentResolver cr, long origId,
+ long groupId) {
cancelThumbnailRequest(cr, origId);
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Return thumbnail representing a specific image item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param groupId the id of group to which this request belongs
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image
- * associated with origId doesn't exist or memory is not enough.
+ * @param imageId the image item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long imageId, long groupId,
int kind, BitmapFactory.Options options) {
- return getThumbnail(cr, origId, kind, options);
+ return getThumbnail(cr, imageId, kind, options);
}
/**
@@ -1059,7 +1090,16 @@
* access.
* <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#loadThumbnail}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
@@ -1509,7 +1549,16 @@
* access.
* <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
@@ -1790,7 +1839,16 @@
/**
* Cached album art.
* <P>Type: TEXT</P>
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#loadThumbnail}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String ALBUM_ART = "album_art";
}
@@ -2009,20 +2067,24 @@
}
/**
- * This class allows developers to query and get two kinds of thumbnails:
- * MINI_KIND: 512 x 384 thumbnail
- * MICRO_KIND: 96 x 96 thumbnail
- *
+ * This class provides utility methods to obtain thumbnails for various
+ * {@link Video} items.
*/
public static class Thumbnails implements BaseColumns {
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver
- * @param origId original video id
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
+ @Deprecated
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
final Uri uri = ContentUris.withAppendedId(
Video.Media.EXTERNAL_CONTENT_URI, origId);
@@ -2030,51 +2092,66 @@
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Return thumbnail representing a specific video item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image
- * associated with origId doesn't exist or memory is not enough.
+ * @param videoId the video item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind,
BitmapFactory.Options options) {
final Uri uri = ContentUris.withAppendedId(
- Video.Media.EXTERNAL_CONTENT_URI, origId);
+ Video.Media.EXTERNAL_CONTENT_URI, videoId);
return InternalThumbnails.getThumbnail(cr, uri, kind, options);
}
/**
- * This method checks if the thumbnails of the specified image (origId) has been created.
- * It will be blocked until the thumbnails are generated.
+ * Cancel any outstanding {@link #getThumbnail} requests, causing
+ * them to return by throwing a {@link OperationCanceledException}.
+ * <p>
+ * This method has no effect on
+ * {@link ContentResolver#loadThumbnail} calls, since they provide
+ * their own {@link CancellationSignal}.
*
- * @param cr ContentResolver used to dispatch queries to MediaProvider.
- * @param origId Original image id associated with thumbnail of interest.
- * @param groupId the id of group to which this request belongs
- * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @return A Bitmap instance. It could be null if the original image associated with
- * origId doesn't exist or memory is not enough.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
- int kind, BitmapFactory.Options options) {
- return getThumbnail(cr, origId, kind, options);
+ @Deprecated
+ public static void cancelThumbnailRequest(ContentResolver cr, long videoId,
+ long groupId) {
+ cancelThumbnailRequest(cr, videoId);
}
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
+ * Return thumbnail representing a specific video item. If a
+ * thumbnail doesn't exist, this method will block until it's
+ * generated. Callers are responsible for their own in-memory
+ * caching of returned values.
*
- * @param cr ContentResolver
- * @param origId original video id
- * @param groupId the same groupId used in getThumbnail.
+ * @param videoId the video item to obtain a thumbnail for.
+ * @param kind optimal thumbnail size desired.
+ * @return decoded thumbnail, or {@code null} if problem was
+ * encountered.
+ * @deprecated Callers should migrate to using
+ * {@link ContentResolver#loadThumbnail}, since it
+ * offers richer control over requested thumbnail sizes
+ * and cancellation behavior.
*/
- public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
- cancelThumbnailRequest(cr, origId);
+ @Deprecated
+ public static Bitmap getThumbnail(ContentResolver cr, long videoId, long groupId,
+ int kind, BitmapFactory.Options options) {
+ return getThumbnail(cr, videoId, kind, options);
}
/**
@@ -2110,14 +2187,17 @@
/**
* Path to the thumbnail file on disk.
* <p>
- * Note that apps may not have filesystem permissions to directly
- * access this path. Instead of trying to open this path directly,
- * apps should use
- * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
- * access.
- * <p>
* Type: TEXT
+ *
+ * @deprecated Apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path
+ * directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * to gain access. This value will always be
+ * {@code NULL} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} or higher.
*/
+ @Deprecated
public static final String DATA = "_data";
/**
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 10d7911..ec63cd9 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1112,7 +1112,6 @@
@Override
protected void playImpl() {
- dispatchOnStart();
super.playImpl();
try {
mFileOutputStream.close();
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 0a2d65c..f9370a8 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -505,7 +505,7 @@
}
mWholeWidth += width;
} else {
- builder.addReplacementRun(mCachedPaint, start, end, width);
+ builder.appendReplacementRun(mCachedPaint, end - start, width);
}
}
@@ -520,7 +520,7 @@
mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
mWidths.getRawArray(), start);
} else {
- builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
+ builder.appendStyleRun(mCachedPaint, end - start, false /* isRtl */);
}
} else {
// If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -536,7 +536,7 @@
mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
isRtl, mWidths.getRawArray(), levelStart);
} else {
- builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
+ builder.appendStyleRun(mCachedPaint, levelEnd - levelStart, isRtl);
}
if (levelEnd == end) {
break;
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 648bd1b..f846a35 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -23,6 +23,7 @@
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 @@
private final Typeface mTypeface;
private final int mTextFontWeight;
+ private final LocaleList mTextLocales;
private final float mShadowRadius;
private final float mShadowDx;
@@ -149,6 +151,19 @@
mTextFontWeight = a.getInt(com.android.internal.R.styleable
.TextAppearance_textFontWeight, -1);
+ final String localeString = a.getString(com.android.internal.R.styleable
+ .TextAppearance_textLocale);
+ if (localeString != null) {
+ LocaleList localeList = LocaleList.forLanguageTags(localeString);
+ if (!localeList.isEmpty()) {
+ mTextLocales = localeList;
+ } else {
+ mTextLocales = null;
+ }
+ } else {
+ mTextLocales = null;
+ }
+
mShadowRadius = a.getFloat(com.android.internal.R.styleable
.TextAppearance_shadowRadius, 0.0f);
mShadowDx = a.getFloat(com.android.internal.R.styleable
@@ -201,6 +216,7 @@
mTypeface = null;
mTextFontWeight = -1;
+ mTextLocales = null;
mShadowRadius = 0.0f;
mShadowDx = 0.0f;
@@ -233,6 +249,7 @@
mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
mTextFontWeight = src.readInt();
+ mTextLocales = src.readParcelable(LocaleList.class.getClassLoader());
mShadowRadius = src.readFloat();
mShadowDx = src.readFloat();
@@ -285,6 +302,7 @@
LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
dest.writeInt(mTextFontWeight);
+ dest.writeParcelable(mTextLocales, flags);
dest.writeFloat(mShadowRadius);
dest.writeFloat(mShadowDx);
@@ -349,6 +367,15 @@
}
/**
+ * Returns the {@link android.os.LocaleList} specified by this span, or <code>null</code>
+ * if it does not specify one.
+ */
+ @Nullable
+ public LocaleList getTextLocales() {
+ return mTextLocales;
+ }
+
+ /**
* Returns the typeface specified by this span, or <code>null</code>
* if it does not specify one.
*/
@@ -487,6 +514,10 @@
ds.setTextSize(mTextSize);
}
+ if (mTextLocales != null) {
+ ds.setTextLocales(mTextLocales);
+ }
+
if (mHasElegantTextHeight) {
ds.setElegantTextHeight(mElegantTextHeight);
}
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 4608e20..0e5252e 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -315,6 +315,7 @@
ArrayList<Transition> currentTransitions =
runningTransitions.get(mSceneRoot);
currentTransitions.remove(transition);
+ transition.removeListener(this);
}
});
mTransition.captureValues(mSceneRoot, false);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a401c6d..e370e11 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -59,7 +59,10 @@
import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
@@ -67,7 +70,6 @@
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -540,17 +542,16 @@
mCurId = res.id;
mBindSequence = res.sequence;
}
- startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
- null, 0, 0, 0);
+ startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
}
case MSG_UNBIND: {
final int sequence = msg.arg1;
- @InputMethodClient.UnbindReason
+ @UnbindReason
final int reason = msg.arg2;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
- " reason=" + InputMethodClient.getUnbindReason(reason));
+ " reason=" + InputMethodDebug.unbindReasonToString(reason));
}
final boolean startInput;
synchronized (mH) {
@@ -567,8 +568,7 @@
}
if (startInput) {
startInputInner(
- InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0,
- 0);
+ StartInputReason.UNBOUND_FROM_IMMS, null, 0, 0, 0);
}
return;
}
@@ -597,9 +597,8 @@
// handling this message.
if (mServedView != null && canStartInput(mServedView)) {
if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
- final int reason = active ?
- InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
- InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
+ final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+ : StartInputReason.DEACTIVATED_BY_IMMS;
startInputInner(reason, null, 0, 0, 0);
}
}
@@ -696,7 +695,7 @@
}
@Override
- public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
+ public void onUnbindMethod(int sequence, @UnbindReason int unbindReason) {
mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
}
@@ -1386,11 +1385,10 @@
mServedConnecting = true;
}
- startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0,
- 0, 0);
+ startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0);
}
- boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
+ boolean startInputInner(@StartInputReason int startInputReason,
@Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
final View view;
@@ -1400,7 +1398,7 @@
// Make sure we have a window token for the served view.
if (DEBUG) {
Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
- " reason=" + InputMethodClient.getStartInputReason(startInputReason));
+ " reason=" + InputMethodDebug.startInputReasonToString(startInputReason));
}
if (view == null) {
if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
@@ -1519,7 +1517,7 @@
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
+ " null. startInputReason="
- + InputMethodClient.getStartInputReason(startInputReason)
+ + InputMethodDebug.startInputReasonToString(startInputReason)
+ " editorInfo=" + tba
+ " controlFlags=#" + Integer.toHexString(controlFlags));
return false;
@@ -1654,7 +1652,7 @@
@UnsupportedAppUsage
public void checkFocus() {
if (checkFocusNoStartInput(false)) {
- startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
+ startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
}
}
@@ -1717,7 +1715,7 @@
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
- + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+ + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " first=" + first + " flags=#"
+ Integer.toHexString(windowFlags));
if (mRestartOnNextWindowFocus) {
@@ -1744,8 +1742,8 @@
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
// smooth.
- if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
- rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
+ if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
+ controlFlags, softInputMode, windowFlags)) {
return;
}
}
@@ -1756,7 +1754,7 @@
try {
if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
mService.startInputOrWindowGainedFocus(
- InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
null, 0 /* missingMethodFlags */,
rootView.getContext().getApplicationInfo().targetSdkVersion);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9692579..2e92f14 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.os.LocaleList;
import android.os.Looper;
@@ -212,34 +211,13 @@
return suggestSelection(request);
}
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- @UnsupportedAppUsage
- default TextSelection suggestSelection(
- @NonNull CharSequence text,
- @IntRange(from = 0) int selectionStartIndex,
- @IntRange(from = 0) int selectionEndIndex,
- @Nullable TextSelection.Options options) {
- if (options == null) {
- return suggestSelection(new TextSelection.Request.Builder(
- text, selectionStartIndex, selectionEndIndex).build());
- } else if (options.getRequest() != null) {
- return suggestSelection(options.getRequest());
- } else {
- return suggestSelection(
- new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex)
- .setDefaultLocales(options.getDefaultLocales())
- .build());
- }
- }
-
/**
* Classifies the specified text and returns a {@link TextClassification} object that can be
* used to generate a widget for handling the classified text.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @param request the text classification request
@@ -262,7 +240,7 @@
* {@link #classifyText(TextClassification.Request)}. If that method calls this method,
* a stack overflow error will happen.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @param text text providing context for the text to classify (which is specified
@@ -292,34 +270,13 @@
return classifyText(request);
}
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- @UnsupportedAppUsage
- default TextClassification classifyText(
- @NonNull CharSequence text,
- @IntRange(from = 0) int startIndex,
- @IntRange(from = 0) int endIndex,
- @Nullable TextClassification.Options options) {
- if (options == null) {
- return classifyText(
- new TextClassification.Request.Builder(text, startIndex, endIndex).build());
- } else if (options.getRequest() != null) {
- return classifyText(options.getRequest());
- } else {
- return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex)
- .setDefaultLocales(options.getDefaultLocales())
- .setReferenceTime(options.getReferenceTime())
- .build());
- }
- }
-
/**
* Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
* links information.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @param request the text links request
@@ -334,27 +291,10 @@
return new TextLinks.Builder(request.getText().toString()).build();
}
- // TODO: Remove once apps can build against the latest sdk.
- /** @hide */
- @UnsupportedAppUsage
- default TextLinks generateLinks(
- @NonNull CharSequence text, @Nullable TextLinks.Options options) {
- if (options == null) {
- return generateLinks(new TextLinks.Request.Builder(text).build());
- } else if (options.getRequest() != null) {
- return generateLinks(options.getRequest());
- } else {
- return generateLinks(new TextLinks.Request.Builder(text)
- .setDefaultLocales(options.getDefaultLocales())
- .setEntityConfig(options.getEntityConfig())
- .build());
- }
- }
-
/**
* Returns the maximal length of text that can be processed by generateLinks.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* @see #generateLinks(TextLinks.Request)
@@ -365,9 +305,29 @@
}
/**
+ * Detects the language of the specified text.
+ *
+ * <p><strong>NOTE: </strong>Call on a worker thread.
+ *
+ *
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+ *
+ * @param request the {@link TextLanguage} request.
+ * @return the {@link TextLanguage} result.
+ */
+ @WorkerThread
+ @NonNull
+ default TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
+ Preconditions.checkNotNull(request);
+ Utils.checkMainThread();
+ return TextLanguage.EMPTY;
+ }
+
+ /**
* Reports a selection event.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*/
default void onSelectionEvent(@NonNull SelectionEvent event) {}
@@ -375,7 +335,7 @@
/**
* Destroys this TextClassifier.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
* throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
*
* <p>Subsequent calls to this method are no-ops.
@@ -385,7 +345,7 @@
/**
* Returns whether or not this TextClassifier has been destroyed.
*
- * <strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
+ * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
* with the classifier and an attempt to do so would throw an {@link IllegalStateException}.
* However, this method should never throw an {@link IllegalStateException}.
*
@@ -396,9 +356,7 @@
}
/** @hide **/
- default void dump(@NonNull IndentingPrintWriter printWriter) {
-
- }
+ default void dump(@NonNull IndentingPrintWriter printWriter) {}
/**
* Configuration object for specifying what entities to identify.
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
new file mode 100644
index 0000000..d28459e
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Represents the result of language detection of a piece of text.
+ * <p>
+ * This contains a list of locales, each paired with a confidence score, sorted in decreasing
+ * order of those scores. E.g., for a given input text, the model may return
+ * {@code [<"en", 0.85>, <"fr", 0.15>]}. This sample result means the model reports that it is
+ * 85% likely that the entire text is in English and 15% likely that the entire text is in French,
+ * etc. It does not mean that 85% of the input is in English and 15% is in French.
+ */
+public final class TextLanguage implements Parcelable {
+
+ public static final Creator<TextLanguage> CREATOR = new Creator<TextLanguage>() {
+ @Override
+ public TextLanguage createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public TextLanguage[] newArray(int size) {
+ return new TextLanguage[size];
+ }
+ };
+
+ static final TextLanguage EMPTY = new Builder().build();
+
+ @Nullable private final String mId;
+ private final EntityConfidence mEntityConfidence;
+ private final Bundle mBundle;
+
+ private TextLanguage(
+ @Nullable String id,
+ EntityConfidence entityConfidence,
+ Bundle bundle) {
+ mId = id;
+ mEntityConfidence = entityConfidence;
+ mBundle = bundle;
+ }
+
+ /**
+ * Returns the id, if one exists, for this object.
+ */
+ @Nullable
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the number of possible locales for the processed text.
+ */
+ @IntRange(from = 0)
+ public int getLocaleHypothesisCount() {
+ return mEntityConfidence.getEntities().size();
+ }
+
+ /**
+ * Returns the language locale at the specified index. Locales are ordered from high
+ * confidence to low confidence.
+ *
+ * @throws IndexOutOfBoundsException if the specified index is out of range.
+ * @see #getLocaleCount() for the number of locales available.
+ */
+ @NonNull
+ public ULocale getLocale(int index) {
+ return ULocale.forLanguageTag(mEntityConfidence.getEntities().get(index));
+ }
+
+ /**
+ * Returns the confidence score for the specified language locale. The value ranges from
+ * 0 (low confidence) to 1 (high confidence). 0 indicates that the locale was not found for
+ * the processed text.
+ */
+ @FloatRange(from = 0.0, to = 1.0)
+ public float getConfidenceScore(@NonNull ULocale locale) {
+ return mEntityConfidence.getConfidenceScore(locale.toLanguageTag());
+ }
+
+ /**
+ * Returns a bundle containing non-structured extra information about this result.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should prefer
+ * to hold a reference to the returned bundle rather than frequently calling this method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mBundle.deepCopy();
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.US,
+ "TextLanguage {id=%s, locales=%s, bundle=%s}",
+ mId, mEntityConfidence, mBundle);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ mEntityConfidence.writeToParcel(dest, flags);
+ dest.writeBundle(mBundle);
+ }
+
+ private static TextLanguage readFromParcel(Parcel in) {
+ return new TextLanguage(
+ in.readString(),
+ EntityConfidence.CREATOR.createFromParcel(in),
+ in.readBundle());
+ }
+
+ /**
+ * Builder used to build TextLanguage objects.
+ */
+ public static final class Builder {
+
+ @Nullable private String mId;
+ private final Map<String, Float> mEntityConfidenceMap = new ArrayMap<>();
+ @Nullable private Bundle mBundle;
+
+ /**
+ * Sets a language locale for the processed text and assigns a confidence score. If the
+ * locale has already been set, this updates it.
+ *
+ * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+ * 0 implies the locale does not exist for the processed text.
+ * Values greater than 1 are clamped to 1.
+ */
+ @NonNull
+ public Builder putLocale(
+ @NonNull ULocale locale,
+ @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+ Preconditions.checkNotNull(locale);
+ mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
+ return this;
+ }
+
+ /**
+ * Sets an optional id for the TextLanguage object.
+ */
+ @NonNull
+ public Builder setId(@Nullable String id) {
+ mId = id;
+ return this;
+ }
+
+ /**
+ * Sets a bundle containing non-structured extra information about the TextLanguage object.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle bundle) {
+ mBundle = Preconditions.checkNotNull(bundle);
+ return this;
+ }
+
+ /**
+ * Builds and returns a new TextLanguage object.
+ * <p>
+ * If necessary, this method will verify fields, clamp them, and make them immutable.
+ */
+ @NonNull
+ public TextLanguage build() {
+ mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+ return new TextLanguage(
+ mId,
+ new EntityConfidence(mEntityConfidenceMap),
+ mBundle);
+ }
+ }
+
+ /**
+ * A request object for detecting the language of a piece of text.
+ */
+ public static final class Request implements Parcelable {
+
+ public static final Creator<Request> CREATOR = new Creator<Request>() {
+ @Override
+ public Request createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public Request[] newArray(int size) {
+ return new Request[size];
+ }
+ };
+
+ private final CharSequence mText;
+ private final Bundle mBundle;
+
+ private Request(CharSequence text, Bundle bundle) {
+ mText = text;
+ mBundle = bundle;
+ }
+
+ /**
+ * Returns the text to process.
+ */
+ @NonNull
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns a bundle containing non-structured extra information about this request.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mBundle.deepCopy();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mText);
+ dest.writeBundle(mBundle);
+ }
+
+ private static Request readFromParcel(Parcel in) {
+ return new Request(
+ in.readCharSequence(),
+ in.readBundle());
+ }
+
+ /**
+ * A builder for building TextLanguage requests.
+ */
+ public static final class Builder {
+
+ private final CharSequence mText;
+ @Nullable private Bundle mBundle;
+
+ /**
+ * Creates a builder to build TextLanguage requests.
+ *
+ * @param text the text to process.
+ */
+ public Builder(@NonNull CharSequence text) {
+ mText = Preconditions.checkNotNull(text);
+ }
+
+ /**
+ * Sets a bundle containing non-structured extra information about the request.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull Bundle bundle) {
+ mBundle = Preconditions.checkNotNull(bundle);
+ return this;
+ }
+
+ /**
+ * Builds and returns a new TextLanguage request object.
+ * <p>
+ * If necessary, this method will verify fields, clamp them, and make them immutable.
+ */
+ @NonNull
+ public Request build() {
+ mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+ return new Request(mText.toString(), mBundle);
+ }
+ }
+ }
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 69d7202..300bb6f 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -299,7 +299,7 @@
/**
* The resource was blocked because it may trick the user into a billing agreement.
*
- * <p>This constant is only used when targetSdkVersion is greater than {@link
+ * <p>This constant is only used when targetSdkVersion is at least {@link
* android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used
* instead.
*/
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f74c234..3dd6fd1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3517,6 +3517,7 @@
ColorStateList mTextColorHint = null;
ColorStateList mTextColorLink = null;
int mTextSize = -1;
+ LocaleList mTextLocales = null;
String mFontFamily = null;
Typeface mFontTypeface = null;
boolean mFontFamilyExplicit = false;
@@ -3543,6 +3544,7 @@
+ " mTextColorHint:" + mTextColorHint + "\n"
+ " mTextColorLink:" + mTextColorLink + "\n"
+ " mTextSize:" + mTextSize + "\n"
+ + " mTextLocales:" + mTextLocales + "\n"
+ " mFontFamily:" + mFontFamily + "\n"
+ " mFontTypeface:" + mFontTypeface + "\n"
+ " mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
@@ -3579,6 +3581,8 @@
com.android.internal.R.styleable.TextAppearance_textColorLink);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_textSize,
com.android.internal.R.styleable.TextAppearance_textSize);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_textLocale,
+ com.android.internal.R.styleable.TextAppearance_textLocale);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_typeface,
com.android.internal.R.styleable.TextAppearance_typeface);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFamily,
@@ -3652,6 +3656,15 @@
attributes.mTextSize =
appearance.getDimensionPixelSize(attr, attributes.mTextSize);
break;
+ case com.android.internal.R.styleable.TextAppearance_textLocale:
+ final String localeString = appearance.getString(attr);
+ if (localeString != null) {
+ final LocaleList localeList = LocaleList.forLanguageTags(localeString);
+ if (!localeList.isEmpty()) {
+ attributes.mTextLocales = localeList;
+ }
+ }
+ break;
case com.android.internal.R.styleable.TextAppearance_typeface:
attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex);
if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
@@ -3738,6 +3751,10 @@
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
+ if (attributes.mTextLocales != null) {
+ setTextLocales(attributes.mTextLocales);
+ }
+
if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
attributes.mFontFamily = null;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
new file mode 100644
index 0000000..a5dc3d1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+
+/**
+ * Provides useful methods for debugging.
+ */
+public final class InputMethodDebug {
+ /**
+ * Not intended to be instantiated.
+ */
+ private InputMethodDebug() {
+ }
+
+ /**
+ * Converts {@link StartInputReason} to {@link String} for debug logging.
+ *
+ * @param reason integer constant for {@link StartInputReason}.
+ * @return {@link String} message corresponds for the given {@code reason}.
+ */
+ public static String startInputReasonToString(@StartInputReason int reason) {
+ switch (reason) {
+ case StartInputReason.UNSPECIFIED:
+ return "UNSPECIFIED";
+ case StartInputReason.WINDOW_FOCUS_GAIN:
+ return "WINDOW_FOCUS_GAIN";
+ case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY:
+ return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
+ case StartInputReason.APP_CALLED_RESTART_INPUT_API:
+ return "APP_CALLED_RESTART_INPUT_API";
+ case StartInputReason.CHECK_FOCUS:
+ return "CHECK_FOCUS";
+ case StartInputReason.BOUND_TO_IMMS:
+ return "BOUND_TO_IMMS";
+ case StartInputReason.UNBOUND_FROM_IMMS:
+ return "UNBOUND_FROM_IMMS";
+ case StartInputReason.ACTIVATED_BY_IMMS:
+ return "ACTIVATED_BY_IMMS";
+ case StartInputReason.DEACTIVATED_BY_IMMS:
+ return "DEACTIVATED_BY_IMMS";
+ case StartInputReason.SESSION_CREATED_BY_IME:
+ return "SESSION_CREATED_BY_IME";
+ default:
+ return "Unknown=" + reason;
+ }
+ }
+
+ /**
+ * Converts {@link UnbindReason} to {@link String} for debug logging.
+ *
+ * @param reason integer constant for {@link UnbindReason}.
+ * @return {@link String} message corresponds for the given {@code reason}.
+ */
+ public static String unbindReasonToString(@UnbindReason int reason) {
+ switch (reason) {
+ case UnbindReason.UNSPECIFIED:
+ return "UNSPECIFIED";
+ case UnbindReason.SWITCH_CLIENT:
+ return "SWITCH_CLIENT";
+ case UnbindReason.SWITCH_IME:
+ return "SWITCH_IME";
+ case UnbindReason.DISCONNECT_IME:
+ return "DISCONNECT_IME";
+ case UnbindReason.NO_IME:
+ return "NO_IME";
+ case UnbindReason.SWITCH_IME_FAILED:
+ return "SWITCH_IME_FAILED";
+ case UnbindReason.SWITCH_USER:
+ return "SWITCH_USER";
+ default:
+ return "Unknown=" + reason;
+ }
+ }
+
+ /**
+ * Converts {@link SoftInputModeFlags} to {@link String} for debug logging.
+ *
+ * @param softInputMode integer constant for {@link SoftInputModeFlags}.
+ * @return {@link String} message corresponds for the given {@code softInputMode}.
+ */
+ public static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
+ final StringBuilder sb = new StringBuilder();
+ final int state = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ final int adjust = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+ final boolean isForwardNav =
+ (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
+
+ switch (state) {
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
+ sb.append("STATE_UNSPECIFIED");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+ sb.append("STATE_UNCHANGED");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+ sb.append("STATE_HIDDEN");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+ sb.append("STATE_ALWAYS_HIDDEN");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+ sb.append("STATE_VISIBLE");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+ sb.append("STATE_ALWAYS_VISIBLE");
+ break;
+ default:
+ sb.append("STATE_UNKNOWN(");
+ sb.append(state);
+ sb.append(")");
+ break;
+ }
+
+ switch (adjust) {
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
+ sb.append("|ADJUST_UNSPECIFIED");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
+ sb.append("|ADJUST_RESIZE");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN:
+ sb.append("|ADJUST_PAN");
+ break;
+ case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
+ sb.append("|ADJUST_NOTHING");
+ break;
+ default:
+ sb.append("|ADJUST_UNKNOWN(");
+ sb.append(adjust);
+ sb.append(")");
+ break;
+ }
+
+ if (isForwardNav) {
+ // This is a special bit that is set by the system only during the window navigation.
+ sb.append("|IS_FORWARD_NAVIGATION");
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
new file mode 100644
index 0000000..a01c459
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link android.view.inputmethod.InputMethodManager} is calling
+ * {@link com.android.internal.view.IInputMethodManager#startInputOrWindowGainedFocus}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+ StartInputReason.UNSPECIFIED,
+ StartInputReason.WINDOW_FOCUS_GAIN,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY,
+ StartInputReason.APP_CALLED_RESTART_INPUT_API,
+ StartInputReason.CHECK_FOCUS,
+ StartInputReason.BOUND_TO_IMMS,
+ StartInputReason.UNBOUND_FROM_IMMS,
+ StartInputReason.ACTIVATED_BY_IMMS,
+ StartInputReason.DEACTIVATED_BY_IMMS,
+ StartInputReason.SESSION_CREATED_BY_IME})
+public @interface StartInputReason {
+ /**
+ * Reason is not specified.
+ */
+ int UNSPECIFIED = 0;
+ /**
+ * {@link android.view.Window} gained focus and it made the focused {@link android.view.View}
+ * to (re)start a new connection.
+ */
+ int WINDOW_FOCUS_GAIN = 1;
+ /**
+ * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
+ * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
+ * this window focus change event.
+ */
+ int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is
+ * either explicitly called by the application or indirectly called by some Framework class
+ * (e.g. {@link android.widget.EditText}).
+ */
+ int APP_CALLED_RESTART_INPUT_API = 3;
+ /**
+ * {@link android.view.View} requested a new connection because of view focus change.
+ */
+ int CHECK_FOCUS = 4;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#onBindMethod}.
+ */
+ int BOUND_TO_IMMS = 5;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+ */
+ int UNBOUND_FROM_IMMS = 6;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#setActive}.
+ */
+ int ACTIVATED_BY_IMMS = 7;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#setActive}.
+ */
+ int DEACTIVATED_BY_IMMS = 8;
+ /**
+ * {@link com.android.server.inputmethod.InputMethodManagerService} is responding to
+ * {@link com.android.internal.view.IInputSessionCallback#sessionCreated}.
+ */
+ int SESSION_CREATED_BY_IME = 9;
+}
diff --git a/core/java/com/android/internal/inputmethod/UnbindReason.java b/core/java/com/android/internal/inputmethod/UnbindReason.java
new file mode 100644
index 0000000..f0f18f1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/UnbindReason.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link com.android.server.inputmethod.InputMethodManagerService} is
+ * calling {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+ UnbindReason.UNSPECIFIED,
+ UnbindReason.SWITCH_CLIENT,
+ UnbindReason.SWITCH_IME,
+ UnbindReason.DISCONNECT_IME,
+ UnbindReason.NO_IME,
+ UnbindReason.SWITCH_IME_FAILED,
+ UnbindReason.SWITCH_USER})
+public @interface UnbindReason {
+ /**
+ * Reason is not specified.
+ */
+ int UNSPECIFIED = 0;
+ /**
+ * When a new IME client becomes active, the previous IME client will unbound from the current
+ * IME.
+ */
+ int SWITCH_CLIENT = 1;
+ /**
+ * Before a new IME becomes active, the current IME client be unbound from the previous IME.
+ */
+ int SWITCH_IME = 2;
+ /**
+ * When the current IME is disconnected, the current IME client will be unbound.
+ */
+ int DISCONNECT_IME = 3;
+ /**
+ * When the system loses the last enabled IME, the current IME client will be unbound.
+ */
+ int NO_IME = 4;
+ /**
+ * When the system failed to switch to another IME, the current IME client will be unbound.
+ */
+ int SWITCH_IME_FAILED = 5;
+ /**
+ * When a new user becomes foreground, the previous IME client will be unbound from the previous
+ * user's active IME.
+ */
+ int SWITCH_USER = 6;
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 46667d1..2442d0b 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -158,7 +158,7 @@
PROC_WAKELOCKS_FORMAT,
nameStringArray, wlData, null);
- name = nameStringArray[0];
+ name = nameStringArray[0].trim();
count = (int) wlData[1];
if (wakeup_sources) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 2618356..17b2bc4 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -24,7 +24,6 @@
*/
oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
- // unbindReason corresponds to InputMethodClient.UnbindReason.
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 34e8501..29c55c2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -54,7 +54,7 @@
// has gained focus, and if 'attribute' is non-null then also does startInput.
// @NonNull
InputBindResult startInputOrWindowGainedFocus(
- /* @InputMethodClient.StartInputReason */ int startInputReason,
+ /* @StartInputReason */ int startInputReason,
in IInputMethodClient client, in IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, IInputContext inputContext,
diff --git a/core/java/com/android/internal/view/InputMethodClient.java b/core/java/com/android/internal/view/InputMethodClient.java
deleted file mode 100644
index bbd33a2..0000000
--- a/core/java/com/android/internal/view/InputMethodClient.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view;
-
-import android.annotation.IntDef;
-import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-
-import java.lang.annotation.Retention;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-public final class InputMethodClient {
- public static final int START_INPUT_REASON_UNSPECIFIED = 0;
- public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN = 1;
- public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
- public static final int START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API = 3;
- public static final int START_INPUT_REASON_CHECK_FOCUS = 4;
- public static final int START_INPUT_REASON_BOUND_TO_IMMS = 5;
- public static final int START_INPUT_REASON_UNBOUND_FROM_IMMS = 6;
- public static final int START_INPUT_REASON_ACTIVATED_BY_IMMS = 7;
- public static final int START_INPUT_REASON_DEACTIVATED_BY_IMMS = 8;
- public static final int START_INPUT_REASON_SESSION_CREATED_BY_IME = 9;
-
- @Retention(SOURCE)
- @IntDef({START_INPUT_REASON_UNSPECIFIED, START_INPUT_REASON_WINDOW_FOCUS_GAIN,
- START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY,
- START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, START_INPUT_REASON_CHECK_FOCUS,
- START_INPUT_REASON_BOUND_TO_IMMS, START_INPUT_REASON_ACTIVATED_BY_IMMS,
- START_INPUT_REASON_DEACTIVATED_BY_IMMS, START_INPUT_REASON_SESSION_CREATED_BY_IME})
- public @interface StartInputReason {}
-
- public static String getStartInputReason(@StartInputReason final int reason) {
- switch (reason) {
- case START_INPUT_REASON_UNSPECIFIED:
- return "UNSPECIFIED";
- case START_INPUT_REASON_WINDOW_FOCUS_GAIN:
- return "WINDOW_FOCUS_GAIN";
- case START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY:
- return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
- case START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API:
- return "APP_CALLED_RESTART_INPUT_API";
- case START_INPUT_REASON_CHECK_FOCUS:
- return "CHECK_FOCUS";
- case START_INPUT_REASON_BOUND_TO_IMMS:
- return "BOUND_TO_IMMS";
- case START_INPUT_REASON_UNBOUND_FROM_IMMS:
- return "UNBOUND_FROM_IMMS";
- case START_INPUT_REASON_ACTIVATED_BY_IMMS:
- return "ACTIVATED_BY_IMMS";
- case START_INPUT_REASON_DEACTIVATED_BY_IMMS:
- return "DEACTIVATED_BY_IMMS";
- case START_INPUT_REASON_SESSION_CREATED_BY_IME:
- return "SESSION_CREATED_BY_IME";
- default:
- return "Unknown=" + reason;
- }
- }
-
- public static final int UNBIND_REASON_UNSPECIFIED = 0;
- public static final int UNBIND_REASON_SWITCH_CLIENT = 1;
- public static final int UNBIND_REASON_SWITCH_IME = 2;
- public static final int UNBIND_REASON_DISCONNECT_IME = 3;
- public static final int UNBIND_REASON_NO_IME = 4;
- public static final int UNBIND_REASON_SWITCH_IME_FAILED = 5;
- public static final int UNBIND_REASON_SWITCH_USER = 6;
-
- @Retention(SOURCE)
- @IntDef({UNBIND_REASON_UNSPECIFIED, UNBIND_REASON_SWITCH_CLIENT, UNBIND_REASON_SWITCH_IME,
- UNBIND_REASON_DISCONNECT_IME, UNBIND_REASON_NO_IME, UNBIND_REASON_SWITCH_IME_FAILED,
- UNBIND_REASON_SWITCH_USER})
- public @interface UnbindReason {}
-
- public static String getUnbindReason(@UnbindReason final int reason) {
- switch (reason) {
- case UNBIND_REASON_UNSPECIFIED:
- return "UNSPECIFIED";
- case UNBIND_REASON_SWITCH_CLIENT:
- return "SWITCH_CLIENT";
- case UNBIND_REASON_SWITCH_IME:
- return "SWITCH_IME";
- case UNBIND_REASON_DISCONNECT_IME:
- return "DISCONNECT_IME";
- case UNBIND_REASON_NO_IME:
- return "NO_IME";
- case UNBIND_REASON_SWITCH_IME_FAILED:
- return "SWITCH_IME_FAILED";
- case UNBIND_REASON_SWITCH_USER:
- return "SWITCH_USER";
- default:
- return "Unknown=" + reason;
- }
- }
-
- public static String softInputModeToString(@SoftInputModeFlags final int softInputMode) {
- final StringBuilder sb = new StringBuilder();
- final int state = softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE;
- final int adjust = softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST;
- final boolean isForwardNav =
- (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
-
- switch (state) {
- case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
- sb.append("STATE_UNSPECIFIED");
- break;
- case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
- sb.append("STATE_UNCHANGED");
- break;
- case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
- sb.append("STATE_HIDDEN");
- break;
- case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
- sb.append("STATE_ALWAYS_HIDDEN");
- break;
- case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
- sb.append("STATE_VISIBLE");
- break;
- case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
- sb.append("STATE_ALWAYS_VISIBLE");
- break;
- default:
- sb.append("STATE_UNKNOWN(");
- sb.append(state);
- sb.append(")");
- break;
- }
-
- switch (adjust) {
- case LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
- sb.append("|ADJUST_UNSPECIFIED");
- break;
- case LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
- sb.append("|ADJUST_RESIZE");
- break;
- case LayoutParams.SOFT_INPUT_ADJUST_PAN:
- sb.append("|ADJUST_PAN");
- break;
- case LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
- sb.append("|ADJUST_NOTHING");
- break;
- default:
- sb.append("|ADJUST_UNKNOWN(");
- sb.append(adjust);
- sb.append(")");
- break;
- }
-
- if (isForwardNav) {
- // This is a special bit that is set by the system only during the window navigation.
- sb.append("|IS_FORWARD_NAVIGATION");
- }
-
- return sb.toString();
- }
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5e62fd3..59c29e2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -45,6 +45,7 @@
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
"android_opengl_EGL14.cpp",
+ "android_opengl_EGL15.cpp",
"android_opengl_EGLExt.cpp",
"android_opengl_GLES10.cpp",
"android_opengl_GLES10Ext.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c05bad2..eada690 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,7 @@
extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);
extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env);
extern int register_android_opengl_jni_EGL14(JNIEnv* env);
+extern int register_android_opengl_jni_EGL15(JNIEnv* env);
extern int register_android_opengl_jni_EGLExt(JNIEnv* env);
extern int register_android_opengl_jni_GLES10(JNIEnv* env);
extern int register_android_opengl_jni_GLES10Ext(JNIEnv* env);
@@ -1367,6 +1368,7 @@
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_EGL14),
+ REG_JNI(register_android_opengl_jni_EGL15),
REG_JNI(register_android_opengl_jni_EGLExt),
REG_JNI(register_android_opengl_jni_GLES10),
REG_JNI(register_android_opengl_jni_GLES10Ext),
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
new file mode 100644
index 0000000..4a30bab
--- /dev/null
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -0,0 +1,557 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/misc.h>
+
+#include <assert.h>
+#include <EGL/egl.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
+{
+ // EGL 1.4 Init
+ jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+ eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal);
+ jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+ eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal);
+ jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+ egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal);
+ jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+ eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
+
+ eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+ eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+ egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+ eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+
+ eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+ eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+ egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+ eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+ jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+ eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+ jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+ eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+ jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
+ eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+
+ jclass eglClass = _env->FindClass("android/opengl/EGL15");
+ jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+ _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+ jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+ _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+ jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+ _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+ // EGL 1.5 init
+ jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+ nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
+
+ jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+ bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
+
+ getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
+ "getBasePointer", "(Ljava/nio/Buffer;)J");
+ getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
+ "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+ getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
+ "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+ positionID = _env->GetFieldID(bufferClass, "position", "I");
+ limitID = _env->GetFieldID(bufferClass, "limit", "I");
+ elementSizeShiftID =
+ _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+ jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+ eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal);
+ jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+ eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
+
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+ eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+ jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+ _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+ jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+ _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *
+getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset)
+{
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+
+ position = _env->GetIntField(buffer, positionID);
+ limit = _env->GetIntField(buffer, limitID);
+ elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+ *remaining = (limit - position) << elementSizeShift;
+ pointer = _env->CallStaticLongMethod(nioAccessClass,
+ getBasePointerID, buffer);
+ if (pointer != 0L) {
+ *array = NULL;
+ return reinterpret_cast<void*>(pointer);
+ }
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
+ getBaseArrayID, buffer);
+ *offset = _env->CallStaticIntMethod(nioAccessClass,
+ getBaseArrayOffsetID, buffer);
+
+ return NULL;
+}
+
+static void
+releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
+{
+ _env->ReleasePrimitiveArrayCritical(array, data,
+ commit ? 0 : JNI_ABORT);
+}
+
+static void *
+fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+ if (obj == NULL){
+ jniThrowException(_env, "java/lang/IllegalArgumentException",
+ "Object is set to null.");
+ }
+
+ jlong handle = _env->CallLongMethod(obj, mid);
+ return reinterpret_cast<void*>(handle);
+}
+
+static jobject
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
+ if (cls == eglimageClass &&
+ (EGLImage)handle == EGL_NO_IMAGE) {
+ return eglNoImageObject;
+ }
+
+ return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
+/* EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jint type, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLSync _returnValue = (EGLSync) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ _returnValue = eglCreateSync(
+ (EGLDisplay)dpy_native,
+ (EGLenum)type,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, eglsyncClass, eglsyncConstructor, _returnValue);
+}
+
+/* EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) */
+static jboolean
+android_eglDestroySync
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync) {
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+ _returnValue = eglDestroySync(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native
+ );
+ return (jboolean)_returnValue;
+}
+
+/* EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) */
+static jint
+android_eglClientWaitSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags, jlong timeout) {
+ EGLint _returnValue = (EGLint) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+ _returnValue = eglClientWaitSync(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)flags,
+ (EGLTime)timeout
+ );
+ return (jint)_returnValue;
+}
+
+/* EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) */
+static jboolean
+android_eglGetSyncAttrib
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint attribute, jlongArray value_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+ EGLAttrib *value_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *value = (EGLAttrib *) 0;
+
+ if (!value_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "value == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(value_ref) - offset;
+ value_base = (EGLAttrib *)
+ _env->GetLongArrayElements(value_ref, (jboolean *)0);
+ value = value_base + offset;
+
+ _returnValue = eglGetSyncAttrib(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)attribute,
+ (EGLAttrib *)value
+ );
+
+exit:
+ if (value_base) {
+ _env->ReleaseLongArrayElements(value_ref, (jlong*)value_base,
+ _exception ? JNI_ABORT: 0);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return (jboolean)_returnValue;
+}
+
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+ (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLDisplay _returnValue = (EGLDisplay) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ _returnValue = eglGetPlatformDisplay(
+ (EGLenum)platform,
+ (void *)native_display,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformWindowSurface
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_window_buf, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ jarray _array = (jarray) 0;
+ jint _bufferOffset = (jint) 0;
+ EGLSurface _returnValue = (EGLSurface) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+ jint _native_windowRemaining;
+ void *native_window = (void *) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _attrib_listRemaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!native_window_buf) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "native_window == null";
+ goto exit;
+ }
+ native_window = (void *)getPointer(_env, native_window_buf, (jarray*)&_array, &_native_windowRemaining, &_bufferOffset);
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ if (native_window == NULL) {
+ char * _native_windowBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+ native_window = (void *) (_native_windowBase + _bufferOffset);
+ }
+ _returnValue = eglCreatePlatformWindowSurface(
+ (EGLDisplay)dpy_native,
+ (EGLConfig)config_native,
+ (void *)native_window,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_array) {
+ releasePointer(_env, _array, native_window, _exception ? JNI_FALSE : JNI_TRUE);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformPixmapSurface
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ jarray _array = (jarray) 0;
+ jint _bufferOffset = (jint) 0;
+ EGLSurface _returnValue = (EGLSurface) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+ jint _native_pixmapRemaining;
+ void *native_pixmap = (void *) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _attrib_listRemaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!native_pixmap_buf) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "native_pixmap == null";
+ goto exit;
+ }
+ native_pixmap = (void *)getPointer(_env, native_pixmap_buf, (jarray*)&_array, &_native_pixmapRemaining, &_bufferOffset);
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ if (native_pixmap == NULL) {
+ char * _native_pixmapBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+ native_pixmap = (void *) (_native_pixmapBase + _bufferOffset);
+ }
+ _returnValue = eglCreatePlatformPixmapSurface(
+ (EGLDisplay)dpy_native,
+ (EGLConfig)config_native,
+ (void *)native_pixmap,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_array) {
+ releasePointer(_env, _array, native_pixmap, _exception ? JNI_FALSE : JNI_TRUE);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */
+static jboolean
+android_eglWaitSync
+ (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags) {
+ EGLBoolean _returnValue = (EGLBoolean) 0;
+ EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+ _returnValue = eglWaitSync(
+ (EGLDisplay)dpy_native,
+ (EGLSync)sync_native,
+ (EGLint)flags
+ );
+ return (jboolean)_returnValue;
+}
+
+static const char *classPathName = "android/opengl/EGL15";
+
+static const JNINativeMethod methods[] = {
+{"_nativeClassInit", "()V", (void*)nativeClassInit },
+{"eglCreateSync", "(Landroid/opengl/EGLDisplay;I[JI)Landroid/opengl/EGLSync;", (void *) android_eglCreateSync },
+{"eglDestroySync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)Z", (void *) android_eglDestroySync },
+{"eglClientWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;IJ)I", (void *) android_eglClientWaitSync },
+{"eglGetSyncAttrib", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I[JI)Z", (void *) android_eglGetSyncAttrib },
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
+{"eglCreatePlatformWindowSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformWindowSurface },
+{"eglCreatePlatformPixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformPixmapSurface },
+{"eglWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I)Z", (void *) android_eglWaitSync },
+};
+
+int register_android_opengl_jni_EGL15(JNIEnv *_env)
+{
+ int err;
+ err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));
+ return err;
+}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cdb65ed..9bedab5 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 9551718..31212a6 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 6256d08..0036186 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -13,31 +13,149 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.content;
-import android.content.ContentResolver;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
-@Suppress // Failing.
-public class ContentResolverTest extends AndroidTestCase {
- private ContentResolver mContentResolver;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ImageDecoder;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Size;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContentResolver = mContext.getContentResolver();
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentResolverTest {
+
+ private ContentResolver mResolver;
+ private IContentProvider mProvider;
+ private ContentProviderClient mClient;
+
+ private int mSize = 256_000;
+ private MemoryFile mImage;
+
+ @Before
+ public void setUp() throws Exception {
+ mResolver = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ mProvider = mock(IContentProvider.class);
+ mClient = new ContentProviderClient(mResolver, mProvider, false);
+
+ mImage = new MemoryFile("temp.png", mSize);
}
- @LargeTest
- public void testCursorFinalizer() throws Exception {
- // TODO: Want a test case that more predictably reproduce this issue. Selected
- // 600 as this causes the problem 100% of the runs on current hw, it might not
- // do so on some other configuration though.
- for (int i = 0; i < 600; i++) {
- mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
- }
+ @After
+ public void tearDown() throws Exception {
+ mImage.close();
+ mImage = null;
+ }
+
+ private void initImage(int width, int height) throws Exception {
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+
+ canvas.drawColor(Color.RED);
+
+ final Paint paint = new Paint();
+ paint.setColor(Color.BLUE);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(0, 0, width / 2, height / 2, paint);
+
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, mImage.getOutputStream());
+
+ final AssetFileDescriptor afd = new AssetFileDescriptor(
+ new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
+ when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd);
+ }
+
+ private static void assertImageAspectAndContents(Bitmap bitmap) {
+ // And correct aspect ratio
+ final int before = (100 * 1280) / 960;
+ final int after = (100 * bitmap.getWidth()) / bitmap.getHeight();
+ assertEquals(before, after);
+
+ // And scaled correctly
+ final int halfX = bitmap.getWidth() / 2;
+ final int halfY = bitmap.getHeight() / 2;
+ assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10));
+ assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10));
+ }
+
+ @Test
+ public void testLoadThumbnail_Normal() throws Exception {
+ initImage(1280, 960);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(1280, 960), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be untouched
+ assertEquals(1280, res.getWidth());
+ assertEquals(960, res.getHeight());
+
+ assertImageAspectAndContents(res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Scaling() throws Exception {
+ initImage(1280, 960);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(320, 240), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be much smaller
+ assertTrue(res.getWidth() <= 640);
+ assertTrue(res.getHeight() <= 480);
+
+ assertImageAspectAndContents(res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Aspect() throws Exception {
+ initImage(1280, 960);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(240, 320), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be much smaller
+ assertTrue(res.getWidth() <= 640);
+ assertTrue(res.getHeight() <= 480);
+
+ assertImageAspectAndContents(res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Tiny() throws Exception {
+ initImage(32, 24);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(320, 240), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be untouched
+ assertEquals(32, res.getWidth());
+ assertEquals(24, res.getHeight());
+
+ assertImageAspectAndContents(res);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
new file mode 100644
index 0000000..75ca769
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for TextLanguage.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class TextLanguageTest {
+
+ private static final float EPSILON = 0.000001f;
+
+ @Test
+ public void testParcel() throws Exception {
+ final String bundleKey = "experiment.int";
+ final Bundle bundle = new Bundle();
+ bundle.putInt(bundleKey, 1234);
+
+ final TextLanguage reference = new TextLanguage.Builder()
+ .setId("id")
+ .setExtras(bundle)
+ .putLocale(ULocale.ENGLISH, 0.8f)
+ .putLocale(ULocale.GERMAN, 0.2f)
+ .build();
+
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ final TextLanguage result = TextLanguage.CREATOR.createFromParcel(parcel);
+
+ assertEquals("id", result.getId());
+ assertEquals(1234, result.getExtras().getInt(bundleKey));
+ assertEquals(2, result.getLocaleHypothesisCount());
+ assertEquals(ULocale.ENGLISH, result.getLocale(0));
+ assertEquals(0.8f, result.getConfidenceScore(ULocale.ENGLISH), EPSILON);
+ assertEquals(ULocale.GERMAN, result.getLocale(1));
+ assertEquals(0.2f, result.getConfidenceScore(ULocale.GERMAN), EPSILON);
+ }
+
+ @Test
+ public void testRequestParcel() throws Exception {
+ final String text = "This is random text";
+ final String bundleKey = "experiment.str";
+ final Bundle bundle = new Bundle();
+ bundle.putString(bundleKey, "bundle");
+
+ final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
+ .setExtras(bundle)
+ .build();
+
+ final Parcel parcel = Parcel.obtain();
+ reference.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ final TextLanguage.Request result = TextLanguage.Request.CREATOR.createFromParcel(parcel);
+
+ assertEquals(text, result.getText());
+ assertEquals("bundle", result.getExtras().getString(bundleKey));
+ }
+}
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index f291e27..ea93501 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Size;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
@@ -168,4 +169,14 @@
x = in.readInt();
y = in.readInt();
}
+
+ /** {@hide} */
+ public static @NonNull Point convert(@NonNull Size size) {
+ return new Point(size.getWidth(), size.getHeight());
+ }
+
+ /** {@hide} */
+ public static @NonNull Size convert(@NonNull Point point) {
+ return new Size(point.x, point.y);
+ }
}
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 8d38f96..1647909 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -38,11 +38,14 @@
* <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 852e66e..3efe655 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -31,6 +31,21 @@
/**
* 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 @@
/**
* 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 @@
private final @NonNull char[] mText;
private boolean mComputeHyphenation = false;
private boolean mComputeLayout = true;
+ private int mCurrentOffset = 0;
/**
* Construct a builder.
@@ -159,35 +188,50 @@
}
/**
- * 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 @@
*
* Once you called build() method, you can't reuse the Builder class again.
* @throws IllegalStateException if this Builder is reused.
+ * @throws IllegalStateException if the whole text is not covered by one or more runs (style
+ * or replacement)
*/
public MeasuredText build() {
ensureNativePtrNoReuse();
+ if (mCurrentOffset != mText.length) {
+ throw new IllegalStateException("Style info has not been provided for all text.");
+ }
try {
long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
mComputeLayout);
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b04194f..753557c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -224,6 +224,7 @@
break;
case PixelStorageType::Heap:
free(mPixelStorage.heap.address);
+ mallopt(M_PURGE, 0);
break;
case PixelStorageType::Hardware:
auto buffer = mPixelStorage.hardware.buffer;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 0022c93..c6e4c154b 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -141,7 +141,7 @@
// select only flags that might affect text layout
flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
- SkPaint::kAutoHinting_Flag | SkPaint::kVerticalText_Flag);
+ SkPaint::kAutoHinting_Flag);
flags |= (hinting << 16);
return flags;
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6d122d7..ee12b91 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1192,15 +1192,17 @@
public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
/**
- * The maximum security level supported by the device. This is the default
- * security level when a session is opened.
+ * Indicates that the maximum security level supported by the device should
+ * be used when opening a session. This is the default security level
+ * selected when a session is opened.
* @hide
*/
public static final int SECURITY_LEVEL_MAX = 6;
/**
- * The maximum security level supported by the device. This is the default
- * security level when a session is opened.
+ * Returns a value that may be passed as a parameter to {@link #openSession(int)}
+ * requesting that the session be opened at the maximum security level of
+ * the device.
*/
public static final int getMaxSecurityLevel() {
return SECURITY_LEVEL_MAX;
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index e67b100..474b671 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -538,8 +538,13 @@
MtpPropertyGroup propertyGroup;
for (MtpStorageManager.MtpObject obj : objs) {
if (property == 0xffffffff) {
+ if (format == 0 && handle != 0 && handle != 0xffffffff) {
+ // return properties based on the object's format
+ format = obj.getFormat();
+ }
// Get all properties supported by this object
- propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat());
+ // format should be the same between get & put
+ propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
int[] propertyList = getSupportedObjectProperties(format);
propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 3870124..fa9ab1f7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -128,6 +128,7 @@
"libmediametrics",
"libmediaplayer2",
"libmediaplayer2-protos",
+ "libmediandk_utils",
"libmediautils",
"libnetd_client", // for setNetworkForUser
"libprotobuf-cpp-lite",
diff --git a/opengl/java/android/opengl/EGL15.java b/opengl/java/android/opengl/EGL15.java
new file mode 100644
index 0000000..9aae6ad
--- /dev/null
+++ b/opengl/java/android/opengl/EGL15.java
@@ -0,0 +1,149 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public class EGL15 {
+
+ public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 0x00000001;
+ public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 0x00000002;
+ public static final int EGL_OPENGL_ES3_BIT = 0x00000040;
+ public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 0x0001;
+ public static final int EGL_GL_COLORSPACE_SRGB = 0x3089;
+ public static final int EGL_GL_COLORSPACE_LINEAR = 0x308A;
+ public static final int EGL_CONTEXT_MAJOR_VERSION = 0x3098;
+ public static final int EGL_CL_EVENT_HANDLE = 0x309C;
+ public static final int EGL_GL_COLORSPACE = 0x309D;
+ public static final int EGL_GL_TEXTURE_2D = 0x30B1;
+ public static final int EGL_GL_TEXTURE_3D = 0x30B2;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x30B3;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x30B4;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x30B5;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x30B6;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x30B7;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x30B8;
+ public static final int EGL_GL_RENDERBUFFER = 0x30B9;
+ public static final int EGL_GL_TEXTURE_LEVEL = 0x30BC;
+ public static final int EGL_GL_TEXTURE_ZOFFSET = 0x30BD;
+ public static final int EGL_IMAGE_PRESERVED = 0x30D2;
+ public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 0x30F0;
+ public static final int EGL_SYNC_STATUS = 0x30F1;
+ public static final int EGL_SIGNALED = 0x30F2;
+ public static final int EGL_UNSIGNALED = 0x30F3;
+ public static final int EGL_TIMEOUT_EXPIRED = 0x30F5;
+ public static final int EGL_CONDITION_SATISFIED = 0x30F6;
+ public static final int EGL_SYNC_TYPE = 0x30F7;
+ public static final int EGL_SYNC_CONDITION = 0x30F8;
+ public static final int EGL_SYNC_FENCE = 0x30F9;
+ public static final int EGL_CONTEXT_MINOR_VERSION = 0x30FB;
+ public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 0x30FD;
+ public static final int EGL_SYNC_CL_EVENT = 0x30FE;
+ public static final int EGL_SYNC_CL_EVENT_COMPLETE = 0x30FF;
+ public static final int EGL_CONTEXT_OPENGL_DEBUG = 0x31B0;
+ public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 0x31B1;
+ public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 0x31B2;
+ public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+ public static final int EGL_NO_RESET_NOTIFICATION = 0x31BE;
+ public static final int EGL_LOSE_CONTEXT_ON_RESET = 0x31BF;
+ public static final int EGL_PLATFORM_ANDROID_KHR = 0x3141;
+ public static final long EGL_FOREVER = 0xFFFFFFFFFFFFFFFFL;
+ public static final EGLImage EGL_NO_IMAGE = null;
+ public static final EGLSync EGL_NO_SYNC = null;
+ public static final EGLContext EGL_NO_CONTEXT = null;
+ public static final EGLDisplay EGL_NO_DISPLAY = null;
+ public static final EGLSurface EGL_NO_SURFACE = null;
+
+ native private static void _nativeClassInit();
+ static {
+ _nativeClassInit();
+ }
+ // C function EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+
+ public static native EGLSync eglCreateSync(
+ EGLDisplay dpy,
+ int type,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+
+ public static native boolean eglDestroySync(
+ EGLDisplay dpy,
+ EGLSync sync
+ );
+
+ // C function EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+
+ public static native int eglClientWaitSync(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int flags,
+ long timeout
+ );
+
+ // C function EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+
+ public static native boolean eglGetSyncAttrib(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int attribute,
+ long[] value,
+ int offset
+ );
+
+ // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+ public static native EGLDisplay eglGetPlatformDisplay(
+ int platform,
+ long native_display,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+
+ public static native EGLSurface eglCreatePlatformWindowSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ java.nio.Buffer native_window,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+
+ public static native EGLSurface eglCreatePlatformPixmapSurface(
+ EGLDisplay dpy,
+ EGLConfig config,
+ java.nio.Buffer native_pixmap,
+ long[] attrib_list,
+ int offset
+ );
+
+ // C function EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+
+ public static native boolean eglWaitSync(
+ EGLDisplay dpy,
+ EGLSync sync,
+ int flags
+ );
+
+}
diff --git a/opengl/java/android/opengl/EGLImage.java b/opengl/java/android/opengl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/java/android/opengl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+ private EGLImage(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLImage)) return false;
+
+ EGLImage that = (EGLImage) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/opengl/java/android/opengl/EGLSync.java b/opengl/java/android/opengl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/java/android/opengl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+ private EGLSync(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLSync)) return false;
+
+ EGLSync that = (EGLSync) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 33cb5964..4518d79 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -30,7 +30,7 @@
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
-import com.android.carrierdefaultapp.R;
+
/**
* This util class provides common logic for carrier actions
*/
@@ -102,7 +102,7 @@
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onDisableAllMeteredApns subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
- telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, !ENABLE);
+ telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(!ENABLE);
}
private static void onEnableAllMeteredApns(Intent intent, Context context) {
@@ -110,7 +110,7 @@
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onEnableAllMeteredApns subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
- telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, ENABLE);
+ telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(ENABLE);
}
private static void onEnableDefaultURLHandler(Context context) {
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index f9dbcd4..5d84d64 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -104,6 +104,6 @@
assertNotNull(pendingIntent);
Rlog.d(TAG, "verify carrier action: disable all metered apns");
- verify(mTelephonyMgr).carrierActionSetMeteredApnsEnabled(eq(subId), eq(false));
+ verify(mTelephonyMgr).setCarrierDataEnabled(eq(false));
}
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 3333e15..8f33a70 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -25,7 +25,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.app.AlarmManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -224,8 +223,9 @@
}
/** A convenience helper for creating an adjustment for an SBN. */
+ @VisibleForTesting
@Nullable
- private Adjustment createEnqueuedNotificationAdjustment(
+ Adjustment createEnqueuedNotificationAdjustment(
@NonNull NotificationEntry entry,
@NonNull ArrayList<Notification.Action> smartActions,
@NonNull ArrayList<CharSequence> smartReplies) {
@@ -237,7 +237,9 @@
signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
}
if (mNotificationCategorizer.shouldSilence(entry)) {
- signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
+ final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance()
+ : IMPORTANCE_LOW;
+ signals.putInt(KEY_IMPORTANCE, importance);
}
return new Adjustment(
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 2820232..2eb005a 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -66,6 +66,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
+import java.util.ArrayList;
public class AssistantTest extends ServiceTestCase<Assistant> {
@@ -466,4 +467,12 @@
assertFalse(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
}
+
+ @Test
+ public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
+ Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry(
+ mPackageManager, sbn, P1C3), new ArrayList<>(), new ArrayList<>());
+ assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
+ }
}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2fbf42f..6378309 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -137,15 +137,9 @@
<integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
<integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
- <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
- <bool name="config_show4GForLTE">true</bool>
-
<!-- Show indicator for Wifi on but not connected. -->
<bool name="config_showWifiIndicatorWhenEnabled">false</bool>
- <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
- <bool name="config_hideLtePlus">false</bool>
-
<!-- The number of milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 0215fda..3fe9944 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -87,6 +87,8 @@
private Runnable mWatchLongPress;
private final long mLongPressTimeout;
+ protected boolean mSwipingInProgress;
+
final private int[] mTmpPos = new int[2];
private final int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
@@ -127,6 +129,10 @@
mDisableHwLayers = disableHwLayers;
}
+ public boolean isSwipingInProgress() {
+ return mSwipingInProgress;
+ }
+
private float getPos(MotionEvent ev) {
return mSwipeDirection == X ? ev.getX() : ev.getY();
}
@@ -318,6 +324,7 @@
if (Math.abs(delta) > mPagingTouchSlop
&& Math.abs(delta) > Math.abs(deltaPerpendicular)) {
if (mCallback.canChildBeDragged(mCurrView)) {
+ mSwipingInProgress = true;
mCallback.onBeginDrag(mCurrView);
mDragging = true;
mInitialTouchPos = getPos(ev);
@@ -437,6 +444,7 @@
wasRemoved = row.isRemoved();
}
if (!mCancelled || wasRemoved) {
+ mSwipingInProgress = false;
mCallback.onChildDismissed(animView);
}
if (endAction != null) {
@@ -626,6 +634,7 @@
!swipedFastEnough() /* useAccelerateInterpolator */);
} else {
// snappity
+ mSwipingInProgress = false;
mCallback.onDragCancelled(mCurrView);
snapChild(mCurrView, 0 /* leftTarget */, velocity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 81208c4..53ebe74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -24,7 +24,10 @@
public interface VisibilityLocationProvider {
/**
- * @return whether the view is in a visible location right now.
+ * Returns whether an ExpandableNotificationRow is in a visible location or not.
+ *
+ * @param row
+ * @return true if row is in a visible location
*/
boolean isInVisibleLocation(ExpandableNotificationRow row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index fa75c71..cfb6d99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -22,6 +22,7 @@
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -31,7 +32,8 @@
* Interface representing the entity that contains notifications. It can have
* notification views added and removed from it, and will manage displaying them to the user.
*/
-public interface NotificationListContainer {
+public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
+ VisibilityLocationProvider {
/**
* Called when a child is being transferred.
@@ -128,14 +130,6 @@
ViewGroup getViewParentForNotification(NotificationData.Entry entry);
/**
- * Called when the height of an expandable view changes.
- *
- * @param view view whose height changed
- * @param animate whether this change should be animated
- */
- void onHeightChanged(ExpandableView view, boolean animate);
-
- /**
* Resets the currently exposed menu view.
*
* @param animate whether to animate the closing/change of menu view
@@ -158,13 +152,6 @@
*/
void cleanUpViewState(View view);
- /**
- * Returns whether an ExpandableNotificationRow is in a visible location or not.
- *
- * @param row
- * @return true if row is in a visible location
- */
- boolean isInVisibleLocation(ExpandableNotificationRow row);
/**
* Sets a listener to listen for changes in notification locations.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index bac42ff..0bc54a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -25,6 +25,8 @@
import android.animation.PropertyValuesHolder;
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WallpaperManager;
import android.content.Context;
@@ -43,10 +45,6 @@
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.ColorUtils;
-
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -69,6 +67,8 @@
import android.widget.OverScroller;
import android.widget.ScrollView;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
@@ -84,6 +84,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
@@ -117,7 +118,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone.AnimationStateHandler;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -142,10 +143,8 @@
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
-public class NotificationStackScrollLayout extends ViewGroup
- implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener,
- OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer,
- ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable {
+public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
+ NotificationListContainer, ConfigurationListener, Dumpable {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
@@ -160,7 +159,6 @@
private ExpandHelper mExpandHelper;
private final NotificationSwipeHelper mSwipeHelper;
- private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
private final Paint mBackgroundPaint = new Paint();
private final boolean mShouldDrawNotificationBackground;
@@ -344,7 +342,7 @@
private float mDimAmount;
private ValueAnimator mDimAnimator;
private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
- private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+ private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mDimAnimator = null;
@@ -485,12 +483,12 @@
mBgColor = context.getColor(R.color.notification_shade_background_color);
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
- mExpandHelper = new ExpandHelper(getContext(), this,
+ mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
- mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(),
- getContext(), new NotificationMenuListener());
+ mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
+ getContext(), mMenuEventListener);
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
@@ -530,7 +528,7 @@
inflateEmptyShadeView();
inflateFooterView();
- mVisualStabilityManager.setVisibilityLocationProvider(this);
+ mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
setLongPressListener(mEntryManager.getNotificationLongClicker());
}
@@ -589,7 +587,7 @@
return false;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationData.Entry entry,
@@ -628,7 +626,7 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
@@ -1245,11 +1243,6 @@
return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
- mLongPressListener = listener;
- }
-
@ShadeViewRefactor(RefactorComponent.ADAPTER)
public void setQsContainer(ViewGroup qsContainer) {
mQsContainer = qsContainer;
@@ -1273,7 +1266,7 @@
return false;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
float localTouchY = touchY - mTempInt2[1];
@@ -1303,16 +1296,8 @@
return closestChild;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
- getLocationOnScreen(mTempInt2);
- return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public ExpandableView getChildAtPosition(float touchX, float touchY) {
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+ private ExpandableView getChildAtPosition(float touchX, float touchY) {
return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
}
@@ -1325,7 +1310,7 @@
* @param requireMinHeight Whether a minimum height is required for a child to be returned.
* @return the child at the given location.
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ExpandableView getChildAtPosition(float touchX, float touchY,
boolean requireMinHeight) {
// find the view under the pointer, accounting for GONE views
@@ -1365,71 +1350,9 @@
return null;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- public boolean canChildBeExpanded(View v) {
- return v instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) v).isExpandable()
- && !((ExpandableNotificationRow) v).areGutsExposed()
- && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
- }
-
- /* Only ever called as a consequence of an expansion gesture in the shade. */
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setUserExpandedChild(View v, boolean userExpanded) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (userExpanded && onKeyguard()) {
- // Due to a race when locking the screen while touching, a notification may be
- // expanded even after we went back to keyguard. An example of this happens if
- // you click in the empty space while expanding a group.
-
- // We also need to un-user lock it here, since otherwise the content height
- // calculated might be wrong. We also can't invert the two calls since
- // un-userlocking it will trigger a layout switch in the content view.
- row.setUserLocked(false);
- updateContentHeight();
- notifyHeightChangeListener(row);
- return;
- }
- row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
- row.onExpandedByGesture(userExpanded);
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setExpansionCancelled(View v) {
- if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setUserLockedChild(View v, boolean userLocked) {
- if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserLocked(userLocked);
- }
- cancelLongPress();
- requestDisallowInterceptTouchEvent(true);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void expansionStateChanged(boolean isExpanding) {
- mExpandingNotification = isExpanding;
- if (!mExpandedInThisMotion) {
- mMaxScrollAfterExpand = mOwnScrollY;
- mExpandedInThisMotion = true;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public int getMaxExpandHeight(ExpandableView view) {
- return view.getMaxContentHeight();
+ private ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+ getLocationOnScreen(mTempInt2);
+ return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -1526,14 +1449,6 @@
return mStatusBarState == StatusBarState.KEYGUARD;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void setSwipingInProgress(boolean isSwiped) {
- mSwipingInProgress = isSwiped;
- if (isSwiped) {
- requestDisallowInterceptTouchEvent(true);
- }
- }
-
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onConfigurationChanged(Configuration newConfig) {
@@ -1567,249 +1482,6 @@
return this;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onTouchEvent(MotionEvent ev) {
- boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
- || ev.getActionMasked() == MotionEvent.ACTION_UP;
- handleEmptySpaceClick(ev);
- boolean expandWantsIt = false;
- if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
- if (isCancelOrUp) {
- mExpandHelper.onlyObserveMovements(false);
- }
- boolean wasExpandingBefore = mExpandingNotification;
- expandWantsIt = mExpandHelper.onTouchEvent(ev);
- if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
- && !mDisallowScrollingInThisMotion) {
- dispatchDownEventToScroller(ev);
- }
- }
- boolean scrollerWantsIt = false;
- if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
- && !mDisallowScrollingInThisMotion) {
- scrollerWantsIt = onScrollTouch(ev);
- }
- boolean horizontalSwipeWantsIt = false;
- if (!mIsBeingDragged
- && !mExpandingNotification
- && !mExpandedInThisMotion
- && !mOnlyScrollingInThisMotion
- && !mDisallowDismissInThisMotion) {
- horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
- }
-
- // Check if we need to clear any snooze leavebehinds
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
- if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
- && guts.getGutsContent() instanceof NotificationSnooze) {
- NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
- if ((ns.isExpanded() && isCancelOrUp)
- || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
- // If the leavebehind is expanded we clear it on the next up event, otherwise we
- // clear it on the next non-horizontal swipe or expand event.
- checkSnoozeLeavebehind();
- }
- }
- if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
- mCheckForLeavebehind = true;
- }
- return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void dispatchDownEventToScroller(MotionEvent ev) {
- MotionEvent downEvent = MotionEvent.obtain(ev);
- downEvent.setAction(MotionEvent.ACTION_DOWN);
- onScrollTouch(downEvent);
- downEvent.recycle();
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onGenericMotionEvent(MotionEvent event) {
- if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
- || mDisallowScrollingInThisMotion) {
- return false;
- }
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_SCROLL: {
- if (!mIsBeingDragged) {
- final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
- if (vscroll != 0) {
- final int delta = (int) (vscroll * getVerticalScrollFactor());
- final int range = getScrollRange();
- int oldScrollY = mOwnScrollY;
- int newScrollY = oldScrollY - delta;
- if (newScrollY < 0) {
- newScrollY = 0;
- } else if (newScrollY > range) {
- newScrollY = range;
- }
- if (newScrollY != oldScrollY) {
- setOwnScrollY(newScrollY);
- return true;
- }
- }
- }
- }
- }
- }
- return super.onGenericMotionEvent(event);
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private boolean onScrollTouch(MotionEvent ev) {
- if (!isScrollingEnabled()) {
- return false;
- }
- if (isInsideQsContainer(ev) && !mIsBeingDragged) {
- return false;
- }
- mForcedScroll = null;
- initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
-
- final int action = ev.getAction();
-
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- if (getChildCount() == 0 || !isInContentBounds(ev)) {
- return false;
- }
- boolean isBeingDragged = !mScroller.isFinished();
- setIsBeingDragged(isBeingDragged);
- /*
- * If being flinged and user touches, stop the fling. isFinished
- * will be false if being flinged.
- */
- if (!mScroller.isFinished()) {
- mScroller.forceFinished(true);
- }
-
- // Remember where the motion event started
- mLastMotionY = (int) ev.getY();
- mDownX = (int) ev.getX();
- mActivePointerId = ev.getPointerId(0);
- break;
- }
- case MotionEvent.ACTION_MOVE:
- final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- if (activePointerIndex == -1) {
- Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
- break;
- }
-
- final int y = (int) ev.getY(activePointerIndex);
- final int x = (int) ev.getX(activePointerIndex);
- int deltaY = mLastMotionY - y;
- final int xDiff = Math.abs(x - mDownX);
- final int yDiff = Math.abs(deltaY);
- if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
- setIsBeingDragged(true);
- if (deltaY > 0) {
- deltaY -= mTouchSlop;
- } else {
- deltaY += mTouchSlop;
- }
- }
- if (mIsBeingDragged) {
- // Scroll to follow the motion event
- mLastMotionY = y;
- int range = getScrollRange();
- if (mExpandedInThisMotion) {
- range = Math.min(range, mMaxScrollAfterExpand);
- }
-
- float scrollAmount;
- if (deltaY < 0) {
- scrollAmount = overScrollDown(deltaY);
- } else {
- scrollAmount = overScrollUp(deltaY, range);
- }
-
- // Calling customOverScrollBy will call onCustomOverScrolled, which
- // sets the scrolling if applicable.
- if (scrollAmount != 0.0f) {
- // The scrolling motion could not be compensated with the
- // existing overScroll, we have to scroll the view
- customOverScrollBy((int) scrollAmount, mOwnScrollY,
- range, getHeight() / 2);
- // If we're scrolling, leavebehinds should be dismissed
- checkSnoozeLeavebehind();
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mIsBeingDragged) {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
- if (shouldOverScrollFling(initialVelocity)) {
- onOverScrollFling(true, initialVelocity);
- } else {
- if (getChildCount() > 0) {
- if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
- float currentOverScrollTop = getCurrentOverScrollAmount(true);
- if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
- fling(-initialVelocity);
- } else {
- onOverScrollFling(false, initialVelocity);
- }
- } else {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
- getScrollRange())) {
- animateScroll();
- }
- }
- }
- }
- mActivePointerId = INVALID_POINTER;
- endDrag();
- }
-
- break;
- case MotionEvent.ACTION_CANCEL:
- if (mIsBeingDragged && getChildCount() > 0) {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
- animateScroll();
- }
- mActivePointerId = INVALID_POINTER;
- endDrag();
- }
- break;
- case MotionEvent.ACTION_POINTER_DOWN: {
- final int index = ev.getActionIndex();
- mLastMotionY = (int) ev.getY(index);
- mDownX = (int) ev.getX(index);
- mActivePointerId = ev.getPointerId(index);
- break;
- }
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
- mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
- break;
- }
- return true;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- protected boolean isInsideQsContainer(MotionEvent ev) {
- return ev.getY() < mQsContainer.getBottom();
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void onOverScrollFling(boolean open, int initialVelocity) {
- if (mOverscrollTopChangedListener != null) {
- mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
- }
- mDontReportNextOverScroll = true;
- setOverScrollAmount(0.0f, true, false);
- }
-
/**
* Perform a scroll upwards and adapt the overscroll amounts accordingly
*
@@ -1817,7 +1489,7 @@
* @return The amount of scrolling to be performed by the scroller,
* not handled by the overScroll amount.
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private float overScrollUp(int deltaY, int range) {
deltaY = Math.max(deltaY, 0);
float currentTopAmount = getCurrentOverScrollAmount(true);
@@ -1876,24 +1548,6 @@
return scrollAmount;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
- MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- // TODO: Make this decision more intelligent.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionY = (int) ev.getY(newPointerIndex);
- mActivePointerId = ev.getPointerId(newPointerIndex);
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
@@ -2636,7 +2290,7 @@
* numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void fling(int velocityY) {
if (getChildCount() > 0) {
int scrollRange = getScrollRange();
@@ -2674,7 +2328,7 @@
* @return Whether a fling performed on the top overscroll edge lead to the expanded
* overScroll view (i.e QS).
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean shouldOverScrollFling(int initialVelocity) {
float topOverScroll = getCurrentOverScrollAmount(true);
return mScrolledToTopOnFirstDown
@@ -2757,7 +2411,7 @@
return Math.max(desiredPadding, mIntrinsicPadding);
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private float getRubberBandFactor(boolean onTop) {
if (!onTop) {
return RUBBER_BAND_FACTOR_NORMAL;
@@ -2777,99 +2431,13 @@
* rubberbanded, false if it is technically an overscroll but rather a motion to expand the
* overscroll view (e.g. expand QS).
*/
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean isRubberbanded(boolean onTop) {
return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
|| !mScrolledToTopOnFirstDown;
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void endDrag() {
- setIsBeingDragged(false);
- recycleVelocityTracker();
-
- if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
- setOverScrollAmount(0, true /* onTop */, true /* animate */);
- }
- if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
- setOverScrollAmount(0, false /* onTop */, true /* animate */);
- }
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
- ev.offsetLocation(sourceView.getX(), sourceView.getY());
- ev.offsetLocation(-targetView.getX(), -targetView.getY());
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- initDownStates(ev);
- handleEmptySpaceClick(ev);
- boolean expandWantsIt = false;
- if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
- expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
- }
- boolean scrollWantsIt = false;
- if (!mSwipingInProgress && !mExpandingNotification) {
- scrollWantsIt = onInterceptTouchEventScroll(ev);
- }
- boolean swipeWantsIt = false;
- if (!mIsBeingDragged
- && !mExpandingNotification
- && !mExpandedInThisMotion
- && !mOnlyScrollingInThisMotion
- && !mDisallowDismissInThisMotion) {
- swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
- }
- // Check if we need to clear any snooze leavebehinds
- boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
- if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
- !expandWantsIt && !scrollWantsIt) {
- mCheckForLeavebehind = false;
- mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
- false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- }
- if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
- mCheckForLeavebehind = true;
- }
- return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void handleEmptySpaceClick(MotionEvent ev) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
- || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
- mTouchIsClick = false;
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
- isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
- mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
- }
- break;
- }
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private void initDownStates(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mExpandedInThisMotion = false;
- mOnlyScrollingInThisMotion = !mScroller.isFinished();
- mDisallowScrollingInThisMotion = false;
- mDisallowDismissInThisMotion = false;
- mTouchIsClick = true;
- mInitialTouchX = ev.getX();
- mInitialTouchY = ev.getY();
- }
- }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -2896,15 +2464,6 @@
mCurrentStackScrollState.removeViewStateForView(child);
}
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- if (disallowIntercept) {
- cancelLongPress();
- }
- }
-
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void onViewRemovedInternal(View child, ViewGroup container) {
if (mChangePositionInProgress) {
@@ -3600,6 +3159,385 @@
mGoToFullShadeNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
+ protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
+ return new StackScrollAlgorithm(context);
+ }
+
+ /**
+ * @return Whether a y coordinate is inside the content.
+ */
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public boolean isInContentBounds(float y) {
+ return y < getHeight() - getEmptyBottomMargin();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+ mLongPressListener = listener;
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || ev.getActionMasked() == MotionEvent.ACTION_UP;
+ handleEmptySpaceClick(ev);
+ boolean expandWantsIt = false;
+ boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+ if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
+ if (isCancelOrUp) {
+ mExpandHelper.onlyObserveMovements(false);
+ }
+ boolean wasExpandingBefore = mExpandingNotification;
+ expandWantsIt = mExpandHelper.onTouchEvent(ev);
+ if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
+ && !mDisallowScrollingInThisMotion) {
+ dispatchDownEventToScroller(ev);
+ }
+ }
+ boolean scrollerWantsIt = false;
+ if (mIsExpanded && !swipingInProgress && !mExpandingNotification
+ && !mDisallowScrollingInThisMotion) {
+ scrollerWantsIt = onScrollTouch(ev);
+ }
+ boolean horizontalSwipeWantsIt = false;
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion
+ && !mDisallowDismissInThisMotion) {
+ horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+ }
+
+ // Check if we need to clear any snooze leavebehinds
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
+ && guts.getGutsContent() instanceof NotificationSnooze) {
+ NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+ if ((ns.isExpanded() && isCancelOrUp)
+ || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+ // If the leavebehind is expanded we clear it on the next up event, otherwise we
+ // clear it on the next non-horizontal swipe or expand event.
+ checkSnoozeLeavebehind();
+ }
+ }
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCheckForLeavebehind = true;
+ }
+ return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void dispatchDownEventToScroller(MotionEvent ev) {
+ MotionEvent downEvent = MotionEvent.obtain(ev);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ onScrollTouch(downEvent);
+ downEvent.recycle();
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification
+ || mDisallowScrollingInThisMotion) {
+ return false;
+ }
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ if (!mIsBeingDragged) {
+ final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ if (vscroll != 0) {
+ final int delta = (int) (vscroll * getVerticalScrollFactor());
+ final int range = getScrollRange();
+ int oldScrollY = mOwnScrollY;
+ int newScrollY = oldScrollY - delta;
+ if (newScrollY < 0) {
+ newScrollY = 0;
+ } else if (newScrollY > range) {
+ newScrollY = range;
+ }
+ if (newScrollY != oldScrollY) {
+ setOwnScrollY(newScrollY);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private boolean onScrollTouch(MotionEvent ev) {
+ if (!isScrollingEnabled()) {
+ return false;
+ }
+ if (isInsideQsContainer(ev) && !mIsBeingDragged) {
+ return false;
+ }
+ mForcedScroll = null;
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ if (getChildCount() == 0 || !isInContentBounds(ev)) {
+ return false;
+ }
+ boolean isBeingDragged = !mScroller.isFinished();
+ setIsBeingDragged(isBeingDragged);
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.forceFinished(true);
+ }
+
+ // Remember where the motion event started
+ mLastMotionY = (int) ev.getY();
+ mDownX = (int) ev.getX();
+ mActivePointerId = ev.getPointerId(0);
+ break;
+ }
+ case MotionEvent.ACTION_MOVE:
+ final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (activePointerIndex == -1) {
+ Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+ break;
+ }
+
+ final int y = (int) ev.getY(activePointerIndex);
+ final int x = (int) ev.getX(activePointerIndex);
+ int deltaY = mLastMotionY - y;
+ final int xDiff = Math.abs(x - mDownX);
+ final int yDiff = Math.abs(deltaY);
+ if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+ setIsBeingDragged(true);
+ if (deltaY > 0) {
+ deltaY -= mTouchSlop;
+ } else {
+ deltaY += mTouchSlop;
+ }
+ }
+ if (mIsBeingDragged) {
+ // Scroll to follow the motion event
+ mLastMotionY = y;
+ int range = getScrollRange();
+ if (mExpandedInThisMotion) {
+ range = Math.min(range, mMaxScrollAfterExpand);
+ }
+
+ float scrollAmount;
+ if (deltaY < 0) {
+ scrollAmount = overScrollDown(deltaY);
+ } else {
+ scrollAmount = overScrollUp(deltaY, range);
+ }
+
+ // Calling customOverScrollBy will call onCustomOverScrolled, which
+ // sets the scrolling if applicable.
+ if (scrollAmount != 0.0f) {
+ // The scrolling motion could not be compensated with the
+ // existing overScroll, we have to scroll the view
+ customOverScrollBy((int) scrollAmount, mOwnScrollY,
+ range, getHeight() / 2);
+ // If we're scrolling, leavebehinds should be dismissed
+ checkSnoozeLeavebehind();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mIsBeingDragged) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+ if (shouldOverScrollFling(initialVelocity)) {
+ onOverScrollFling(true, initialVelocity);
+ } else {
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ float currentOverScrollTop = getCurrentOverScrollAmount(true);
+ if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ fling(-initialVelocity);
+ } else {
+ onOverScrollFling(false, initialVelocity);
+ }
+ } else {
+ if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ getScrollRange())) {
+ animateScroll();
+ }
+ }
+ }
+ }
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ }
+
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsBeingDragged && getChildCount() > 0) {
+ if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+ animateScroll();
+ }
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int index = ev.getActionIndex();
+ mLastMotionY = (int) ev.getY(index);
+ mDownX = (int) ev.getX(index);
+ mActivePointerId = ev.getPointerId(index);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+ mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
+ break;
+ }
+ return true;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ protected boolean isInsideQsContainer(MotionEvent ev) {
+ return ev.getY() < mQsContainer.getBottom();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void onOverScrollFling(boolean open, int initialVelocity) {
+ if (mOverscrollTopChangedListener != null) {
+ mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+ }
+ mDontReportNextOverScroll = true;
+ setOverScrollAmount(0.0f, true, false);
+ }
+
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+ MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ // TODO: Make this decision more intelligent.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastMotionY = (int) ev.getY(newPointerIndex);
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void endDrag() {
+ setIsBeingDragged(false);
+
+ recycleVelocityTracker();
+
+ if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+ setOverScrollAmount(0, true /* onTop */, true /* animate */);
+ }
+ if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+ setOverScrollAmount(0, false /* onTop */, true /* animate */);
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
+ ev.offsetLocation(sourceView.getX(), sourceView.getY());
+ ev.offsetLocation(-targetView.getX(), -targetView.getY());
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ initDownStates(ev);
+ handleEmptySpaceClick(ev);
+ boolean expandWantsIt = false;
+ boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+ if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
+ expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+ }
+ boolean scrollWantsIt = false;
+ if (!swipingInProgress && !mExpandingNotification) {
+ scrollWantsIt = onInterceptTouchEventScroll(ev);
+ }
+ boolean swipeWantsIt = false;
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion
+ && !mDisallowDismissInThisMotion) {
+ swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+ }
+ // Check if we need to clear any snooze leavebehinds
+ boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+ !expandWantsIt && !scrollWantsIt) {
+ mCheckForLeavebehind = false;
+ mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ }
+ if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCheckForLeavebehind = true;
+ }
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void handleEmptySpaceClick(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
+ || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
+ mTouchIsClick = false;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
+ isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
+ mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
+ }
+ break;
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private void initDownStates(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mExpandedInThisMotion = false;
+ mOnlyScrollingInThisMotion = !mScroller.isFinished();
+ mDisallowScrollingInThisMotion = false;
+ mDisallowDismissInThisMotion = false;
+ mTouchIsClick = true;
+ mInitialTouchX = ev.getX();
+ mInitialTouchY = ev.getY();
+ }
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ if (disallowIntercept) {
+ cancelLongPress();
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.INPUT)
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
if (!isScrollingEnabled()) {
@@ -3710,11 +3648,6 @@
return mIsBeingDragged;
}
- @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context);
- }
-
/**
* @return Whether the specified motion event is actually happening over the content.
*/
@@ -3723,13 +3656,6 @@
return isInContentBounds(event.getY());
}
- /**
- * @return Whether a y coordinate is inside the content.
- */
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean isInContentBounds(float y) {
- return y < getHeight() - getEmptyBottomMargin();
- }
@VisibleForTesting
@ShadeViewRefactor(RefactorComponent.INPUT)
@@ -3742,6 +3668,83 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void requestDisallowLongPress() {
+ cancelLongPress();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void requestDisallowDismiss() {
+ mDisallowDismissInThisMotion = true;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void cancelLongPress() {
+ mSwipeHelper.cancelLongPress();
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
+ mOnEmptySpaceClickListener = listener;
+ }
+
+ /** @hide */
+ @Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (super.performAccessibilityActionInternal(action, arguments)) {
+ return true;
+ }
+ if (!isEnabled()) {
+ return false;
+ }
+ int direction = -1;
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ // fall through
+ case android.R.id.accessibilityActionScrollDown:
+ direction = 1;
+ // fall through
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ // fall through
+ case android.R.id.accessibilityActionScrollUp:
+ final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+ - mShelf.getIntrinsicHeight();
+ final int targetScrollY = Math.max(0,
+ Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+ if (targetScrollY != mOwnScrollY) {
+ mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+ animateScroll();
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+ NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+ View translatingParentView = mSwipeHelper.getTranslatingParentView();
+ View view = null;
+ if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+ // Only close visible guts if they're not a leavebehind.
+ view = guts;
+ } else if (menuRow != null && menuRow.isMenuVisible()
+ && translatingParentView != null) {
+ // Checking menu
+ view = translatingParentView;
+ }
+ if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+ // Touch was outside visible guts / menu notification, close what's visible
+ mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ resetExposedMenuView(true /* animate */, true /* force */);
+ }
+ }
+
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -3760,21 +3763,6 @@
}
}
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void requestDisallowLongPress() {
- cancelLongPress();
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void requestDisallowDismiss() {
- mDisallowDismissInThisMotion = true;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void cancelLongPress() {
- mSwipeHelper.cancelLongPress();
- }
-
@Override
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public boolean isScrolledToTop() {
@@ -3916,7 +3904,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
@@ -3936,7 +3923,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onReset(ExpandableView view) {
updateAnimationState(view);
updateChronometerForChild(view);
@@ -3969,13 +3955,8 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setOnHeightChangedListener(
- ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
- this.mOnHeightChangedListener = mOnHeightChangedListener;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
- mOnEmptySpaceClickListener = listener;
+ ExpandableView.OnHeightChangedListener onHeightChangedListener) {
+ this.mOnHeightChangedListener = onHeightChangedListener;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4465,7 +4446,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setGroupManager(NotificationGroupManager groupManager) {
this.mGroupManager = groupManager;
- mGroupManager.setOnGroupChangeListener(this);
+ mGroupManager.setOnGroupChangeListener(mOnGroupChangeListener);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4508,33 +4489,6 @@
return touchY > mTopPadding + mStackTranslation;
}
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
- boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
- && (mIsExpanded || changedRow.isPinned());
- if (animated) {
- mExpandedGroupView = changedRow;
- mNeedsAnimation = true;
- }
- changedRow.setChildrenExpanded(expanded, animated);
- if (!mGroupExpandedForMeasure) {
- onHeightChanged(changedRow, false /* needsAnimation */);
- }
- runAfterAnimationFinished(new Runnable() {
- @Override
- public void run() {
- changedRow.onFinishedExpansionChange();
- }
- });
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
- mStatusBar.requestNotificationUpdate();
- }
-
/** @hide */
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4567,46 +4521,6 @@
info.setClassName(ScrollView.class.getName());
}
- /** @hide */
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (super.performAccessibilityActionInternal(action, arguments)) {
- return true;
- }
- if (!isEnabled()) {
- return false;
- }
- int direction = -1;
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
- // fall through
- case android.R.id.accessibilityActionScrollDown:
- direction = 1;
- // fall through
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
- // fall through
- case android.R.id.accessibilityActionScrollUp:
- final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
- - mShelf.getIntrinsicHeight();
- final int targetScrollY = Math.max(0,
- Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
- if (targetScrollY != mOwnScrollY) {
- mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
- animateScroll();
- return true;
- }
- break;
- }
- return false;
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void onGroupsChanged() {
- mStatusBar.requestNotificationUpdate();
- }
-
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void generateChildOrderChangedEvent() {
if (mIsExpanded && mAnimationsEnabled) {
@@ -4649,7 +4563,7 @@
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mHeadsUpManager.addListener(mRoundnessManager);
- mHeadsUpManager.setAnimationStateHandler(this);
+ mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -5168,67 +5082,7 @@
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
-
-
- /* Only ever called as a consequence of a lockscreen expansion gesture. */
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean onDraggedDown(View startingChild, int dragLengthY) {
- if (mStatusBarState == StatusBarState.KEYGUARD
- && hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_SHADE,
- (int) (dragLengthY / mDisplayMetrics.density),
- 0 /* velocityDp - N/A */);
-
- // We have notifications, go to locked shade.
- mStatusBar.goToLockedShade(startingChild);
- if (startingChild instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
- row.onExpandedByGesture(true /* drag down is always an open */);
- }
- return true;
- } else {
- // abort gesture.
- return false;
- }
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onDragDownReset() {
- setDimmed(true /* dimmed */, true /* animated */);
- resetScrollPosition();
- resetCheckSnoozeLeavebehind();
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onCrossedThreshold(boolean above) {
- setDimmed(!above /* dimmed */, true /* animate */);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void onTouchSlopExceeded() {
- cancelLongPress();
- checkSnoozeLeavebehind();
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void setEmptyDragAmount(float amount) {
- mNotificationPanel.setEmptyDragAmount(amount);
- }
-
- @Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public boolean isFalsingCheckNeeded() {
- return mStatusBarState == StatusBarState.KEYGUARD;
- }
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateSpeedBumpIndex() {
int speedBumpIndex = 0;
int currentIndex = 0;
@@ -5269,30 +5123,6 @@
mSwipeHelper.resetExposedMenuView(animate, force);
}
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void closeControlsIfOutsideTouch(MotionEvent ev) {
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
- NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
- View translatingParentView = mSwipeHelper.getTranslatingParentView();
- View view = null;
- if (guts != null && !guts.getGutsContent().isLeavebehind()) {
- // Only close visible guts if they're not a leavebehind.
- view = guts;
- } else if (menuRow != null && menuRow.isMenuVisible()
- && translatingParentView != null) {
- // Checking menu
- view = translatingParentView;
- }
- if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
- // Touch was outside visible guts / menu notification, close what's visible
- mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
- false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- resetExposedMenuView(true /* animate */, true /* force */);
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
static class AnimationEvent {
@@ -5614,9 +5444,9 @@
}
};
- class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuClicked(View view, int x, int y, MenuItem item) {
if (mLongPressListener == null) {
return;
@@ -5630,7 +5460,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuReset(View row) {
View translatingParentView = mSwipeHelper.getTranslatingParentView();
if (translatingParentView != null && row == translatingParentView) {
@@ -5640,7 +5469,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuShown(View row) {
if (row instanceof ExpandableNotificationRow) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
@@ -5649,9 +5477,11 @@
}
mSwipeHelper.onMenuShown(row);
}
- }
+ };
- class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+ new NotificationSwipeHelper.NotificationCallback() {
@Override
public void onDismiss() {
mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
@@ -5671,10 +5501,8 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onDragCancelled(View v) {
mFalsingManager.onNotificatonStopDismissing();
- setSwipingInProgress(false);
}
/**
@@ -5682,7 +5510,6 @@
* re-invoking dismiss logic in case the notification has not made its way out yet).
*/
@Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onChildDismissed(View view) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (!row.isDismissed()) {
@@ -5701,7 +5528,6 @@
* @param view view (e.g. notification) to dismiss from the layout
*/
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void handleChildViewDismissed(View view) {
if (mDismissAllInProgress) {
return;
@@ -5709,7 +5535,6 @@
boolean isBlockingHelperShown = false;
- setSwipingInProgress(false);
if (mDragAnimPendingChildren.contains(view)) {
// We start the swipe and finish it in the same frame; we don't want a drag
// animation.
@@ -5743,13 +5568,11 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean isAntiFalsingNeeded() {
return onKeyguard();
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public View getChildAtPosition(MotionEvent ev) {
View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
ev.getY());
@@ -5772,10 +5595,8 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
- setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
updateContinuousShadowDrawing();
if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
@@ -5786,7 +5607,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onChildSnappedBack(View animView, float targetLeft) {
mAmbientState.onDragFinished(animView);
updateContinuousShadowDrawing();
@@ -5808,7 +5628,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean updateSwipeProgress(View animView, boolean dismissable,
float swipeProgress) {
// Returning true prevents alpha fading.
@@ -5816,7 +5635,6 @@
}
@Override
- @ShadeViewRefactor(RefactorComponent.INPUT)
public float getFalsingThresholdFactor() {
return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@@ -5825,5 +5643,197 @@
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 028957d..599da3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -31,11 +31,9 @@
import com.android.systemui.SwipeHelper;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT)
class NotificationSwipeHelper extends SwipeHelper
implements NotificationSwipeActionHelper {
@VisibleForTesting
@@ -229,6 +227,7 @@
if (mCallback.isExpanded()) {
// We don't want to quick-dismiss when it's a heads up as this might lead to closing
// of the panel early.
+ mSwipingInProgress = false;
mCallback.handleChildViewDismissed(view);
}
mCallback.onDismiss();
@@ -248,6 +247,7 @@
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
superSnapChild(animView, targetLeft, velocity);
+ mSwipingInProgress = false;
mCallback.onDragCancelled(animView);
if (targetLeft == 0) {
handleMenuCoveredOrDismissed();
@@ -354,6 +354,7 @@
public void onMenuShown(View animView) {
setExposedMenuView(getTranslatingParentView());
+ mSwipingInProgress = false;
mCallback.onDragCancelled(animView);
Handler handler = getHandler();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 4df1e3b..e4a5caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -32,7 +32,7 @@
public class HeadsUpTouchHelper implements Gefingerpoken {
private HeadsUpManagerPhone mHeadsUpManager;
- private NotificationStackScrollLayout mStackScroller;
+ private Callback mCallback;
private int mTrackingPointer;
private float mTouchSlop;
private float mInitialTouchX;
@@ -44,12 +44,12 @@
private ExpandableNotificationRow mPickedChild;
public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
- NotificationStackScrollLayout stackScroller,
+ Callback callback,
NotificationPanelView notificationPanelView) {
mHeadsUpManager = headsUpManager;
- mStackScroller = stackScroller;
+ mCallback = callback;
mPanel = notificationPanelView;
- Context context = stackScroller.getContext();
+ Context context = mCallback.getContext();
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
}
@@ -75,13 +75,13 @@
mInitialTouchY = y;
mInitialTouchX = x;
setTrackingHeadsUp(false);
- ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
+ ExpandableView child = mCallback.getChildAtRawPosition(x, y);
mTouchingHeadsUpView = false;
if (child instanceof ExpandableNotificationRow) {
mPickedChild = (ExpandableNotificationRow) child;
- mTouchingHeadsUpView = !mStackScroller.isExpanded()
+ mTouchingHeadsUpView = !mCallback.isExpanded()
&& mPickedChild.isHeadsUp() && mPickedChild.isPinned();
- } else if (child == null && !mStackScroller.isExpanded()) {
+ } else if (child == null && !mCallback.isExpanded()) {
// We might touch above the visible heads up child, but then we still would
// like to capture it.
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
@@ -174,4 +174,10 @@
mPickedChild = null;
mTouchingHeadsUpView = false;
}
+
+ public interface Callback {
+ ExpandableView getChildAtRawPosition(float touchX, float touchY);
+ boolean isExpanded();
+ Context getContext();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 980ba87..e92656ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -60,7 +60,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
@@ -71,7 +70,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -140,6 +138,7 @@
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
private final ContextualButtonGroup mContextualButtonGroup;
private Configuration mConfiguration;
+ private Configuration mTmpLastConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
private RecentsOnboarding mRecentsOnboarding;
@@ -286,6 +285,7 @@
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mConfiguration = new Configuration();
+ mTmpLastConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
mScreenPinningNotify = new ScreenPinningNotify(mContext);
@@ -445,13 +445,13 @@
}
private void reloadNavIcons() {
- updateIcons(Configuration.EMPTY, mConfiguration);
+ updateIcons(Configuration.EMPTY);
}
- private void updateIcons(Configuration oldConfig, Configuration newConfig) {
- final boolean orientationChange = oldConfig.orientation != newConfig.orientation;
- final boolean densityChange = oldConfig.densityDpi != newConfig.densityDpi;
- final boolean dirChange = oldConfig.getLayoutDirection() != newConfig.getLayoutDirection();
+ private void updateIcons(Configuration oldConfig) {
+ final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
+ final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
+ final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
if (orientationChange || densityChange) {
mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked);
@@ -485,7 +485,7 @@
private void orientBackButton(KeyButtonDrawable drawable) {
final boolean useAltBack =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
- final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float degrees = useAltBack
? (isRtl ? 270 : -90)
: (isRtl ? 180 : 0);
@@ -946,26 +946,27 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- boolean uiCarModeChanged = updateCarMode(newConfig);
+ mTmpLastConfiguration.updateFrom(mConfiguration);
+ mConfiguration.updateFrom(newConfig);
+ boolean uiCarModeChanged = updateCarMode();
updateTaskSwitchHelper();
- updateIcons(mConfiguration, newConfig);
+ updateIcons(mTmpLastConfiguration);
updateRecentsIcon();
- mRecentsOnboarding.onConfigurationChanged(newConfig);
- if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi
- || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) {
+ mRecentsOnboarding.onConfigurationChanged(mConfiguration);
+ if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
+ || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
// If car mode or density changes, we need to reset the icons.
updateNavButtonIcons();
}
- mConfiguration.updateFrom(newConfig);
}
/**
* If the configuration changed, update the carmode and return that it was updated.
*/
- private boolean updateCarMode(Configuration newConfig) {
+ private boolean updateCarMode() {
boolean uiCarModeChanged = false;
- if (newConfig != null) {
- int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
+ if (mConfiguration != null) {
+ int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
if (isCarMode != mInCarMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6d53cd3..7507702 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 @@
@Override
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
super.setHeadsUpManager(headsUpManager);
- mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
- this);
+ mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
+ mNotificationStackScroller.getHeadsUpCallback(), this);
}
public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 45b32c7..ad9b9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -58,6 +58,7 @@
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.Dependency;
+import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.DragDownHelper;
@@ -182,6 +183,11 @@
}
}
+ @VisibleForTesting
+ protected NotificationStackScrollLayout getStackScrollLayout() {
+ return mStackScrollLayout;
+ }
+
@Override
public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
@@ -215,8 +221,11 @@
public void setService(StatusBar service) {
mService = service;
- setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout,
- mStackScrollLayout));
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback();
+ DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback();
+ setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback,
+ dragDownCallback));
}
@VisibleForTesting
@@ -309,7 +318,7 @@
}
}
if (isDown) {
- mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+ getStackScrollLayout().closeControlsIfOutsideTouch(ev);
}
if (mService.isDozing()) {
mService.mDozeScrimController.extendPulse();
@@ -331,13 +340,14 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mService.isDozing() && !mStackScrollLayout.hasPulsingNotifications()) {
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) {
// Capture all touch events in always-on.
return true;
}
boolean intercept = false;
if (mNotificationPanel.isFullyExpanded()
- && mStackScrollLayout.getVisibility() == View.VISIBLE
+ && stackScrollLayout.getVisibility() == View.VISIBLE
&& mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& !mService.isBouncerShowing()
&& !mService.isDozing()) {
@@ -349,7 +359,7 @@
if (intercept) {
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
- mStackScrollLayout.onInterceptTouchEvent(cancellation);
+ stackScrollLayout.onInterceptTouchEvent(cancellation);
mNotificationPanel.onInterceptTouchEvent(cancellation);
cancellation.recycle();
}
@@ -391,8 +401,9 @@
}
public void cancelExpandHelper() {
- if (mStackScrollLayout != null) {
- mStackScrollLayout.cancelExpandHelper();
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ if (stackScrollLayout != null) {
+ stackScrollLayout.cancelExpandHelper();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 24a28cb..70a3589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -461,6 +461,8 @@
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.handleBroadcast(intent);
}
+ mConfig = Config.readConfig(mContext);
+ mReceiverHandler.post(this::handleConfigurationChanged);
break;
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
@@ -1042,18 +1044,23 @@
config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
config.alwaysShowCdmaRssi =
res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
- config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
config.hspaDataDistinguishable =
res.getBoolean(R.bool.config_hspa_data_distinguishable);
- config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
CarrierConfigManager configMgr = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configMgr.getConfig();
+ // Handle specific carrier config values for the default data SIM
+ int defaultDataSubId = SubscriptionManager.from(context)
+ .getDefaultDataSubscriptionId();
+ PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId);
if (b != null) {
config.alwaysShowDataRatIcon = b.getBoolean(
CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
+ config.show4gForLte = b.getBoolean(
+ CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
+ config.hideLtePlus = b.getBoolean(
+ CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
}
return config;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 15c18e9..6b4ccc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -218,6 +218,9 @@
public void generateChildOrderChangedEvent() {}
@Override
+ public void onReset(ExpandableView view) {}
+
+ @Override
public int getContainerChildCount() {
return mRows.size();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 445a194..46335dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -17,11 +17,11 @@
package com.android.systemui.statusbar.phone;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.MotionEvent;
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import org.junit.Before;
import org.junit.Test;
@@ -43,11 +44,14 @@
private StatusBarWindowView mView;
private StatusBar mStatusBar;
private DragDownHelper mDragDownHelper;
+ private NotificationStackScrollLayout mStackScrollLayout;
@Before
public void setUp() {
mDependency.injectMockDependency(StatusBarStateController.class);
- mView = new StatusBarWindowView(getContext(), null);
+ mView = spy(new StatusBarWindowView(getContext(), null));
+ mStackScrollLayout = mock(NotificationStackScrollLayout.class);
+ when(mView.getStackScrollLayout()).thenReturn(mStackScrollLayout);
mStatusBar = mock(StatusBar.class);
mView.setService(mStatusBar);
mDragDownHelper = mock(DragDownHelper.class);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b750d79..1c8d99a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -189,6 +191,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -257,6 +260,14 @@
@GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
+ /**
+ * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
+ * handler thread, they don't need a lock.
+ */
+ private SparseIntArray mUidRules = new SparseIntArray();
+ /** Flag indicating if background data is restricted. */
+ private boolean mRestrictBackground;
+
final private Context mContext;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -419,6 +430,16 @@
// Handle private DNS validation status updates.
private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
+ /**
+ * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
+ */
+ private static final int EVENT_UID_RULES_CHANGED = 39;
+
+ /**
+ * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
+ */
+ private static final int EVENT_DATA_SAVER_CHANGED = 40;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -780,6 +801,9 @@
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ // To ensure uid rules are synchronized with Network Policy, register for
+ // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
+ // reading existing policy from disk.
try {
mPolicyManager.registerListener(mPolicyListener);
} catch (RemoteException e) {
@@ -910,7 +934,8 @@
registerPrivateDnsSettingsCallbacks();
}
- private Tethering makeTethering() {
+ @VisibleForTesting
+ protected Tethering makeTethering() {
// TODO: Move other elements into @Overridden getters.
final TetheringDependencies deps = new TetheringDependencies() {
@Override
@@ -1116,11 +1141,6 @@
if (ignoreBlocked) {
return false;
}
- // Networks are never blocked for system services
- // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked.
- if (isSystem(uid)) {
- return false;
- }
synchronized (mVpns) {
final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
if (vpn != null && vpn.isBlockingUid(uid)) {
@@ -1150,6 +1170,17 @@
mNetworkInfoBlockingLogs.log(action + " " + uid);
}
+ private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
+ boolean blocked) {
+ if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
+ return;
+ }
+ String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
+ nri.mUid, nri.request.requestId, net.netId));
+ mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+ }
+
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1651,10 +1682,17 @@
private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
- // TODO: notify UID when it has requested targeted updates
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
}
@Override
public void onRestrictBackgroundChanged(boolean restrictBackground) {
+ // caller is NPMS, since we only register with them
+ if (LOGD_BLOCKED_NETWORKINFO) {
+ log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+
// TODO: relocate this specific callback in Tethering.
if (restrictBackground) {
log("onRestrictBackgroundChanged(true): disabling tethering");
@@ -1663,6 +1701,50 @@
}
};
+ void handleUidRulesChanged(int uid, int newRules) {
+ // skip update when we've already applied rules
+ final int oldRules = mUidRules.get(uid, RULE_NONE);
+ if (oldRules == newRules) return;
+
+ maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
+
+ if (newRules == RULE_NONE) {
+ mUidRules.delete(uid);
+ } else {
+ mUidRules.put(uid, newRules);
+ }
+ }
+
+ void handleRestrictBackgroundChanged(boolean restrictBackground) {
+ if (mRestrictBackground == restrictBackground) return;
+
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ final boolean curMetered = nai.networkCapabilities.isMetered();
+ maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+ restrictBackground);
+ }
+
+ mRestrictBackground = restrictBackground;
+ }
+
+ private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted) {
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+ // Because the return value of this function depends on the list of UIDs the
+ // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
+ // list all state depending on the return value of this function has to be recomputed.
+ // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
+ // send the necessary onBlockedStatusChanged callbacks.
+ if (vpn != null && vpn.isBlockingUid(uid)) {
+ return true;
+ }
+ }
+
+ return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+ isNetworkMetered, isBackgroundRestricted);
+ }
+
/**
* Require that the caller is either in the same user or has appropriate permission to interact
* across users.
@@ -2118,6 +2200,28 @@
pw.decreaseIndent();
pw.println();
+ pw.print("Restrict background: ");
+ pw.println(mRestrictBackground);
+ pw.println();
+
+ pw.println("Status for known UIDs:");
+ pw.increaseIndent();
+ final int size = mUidRules.size();
+ for (int i = 0; i < size; i++) {
+ // Don't crash if the array is modified while dumping in bugreports.
+ try {
+ final int uid = mUidRules.keyAt(i);
+ final int uidRules = mUidRules.get(uid, RULE_NONE);
+ pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ pw.println(" ArrayIndexOutOfBoundsException");
+ } catch (ConcurrentModificationException e) {
+ pw.println(" ConcurrentModificationException");
+ }
+ }
+ pw.println();
+ pw.decreaseIndent();
+
pw.println("Network Requests:");
pw.increaseIndent();
dumpNetworkRequests(pw);
@@ -3195,6 +3299,12 @@
handlePrivateDnsValidationUpdate(
(PrivateDnsValidationUpdate) msg.obj);
break;
+ case EVENT_UID_RULES_CHANGED:
+ handleUidRulesChanged(msg.arg1, msg.arg2);
+ break;
+ case EVENT_DATA_SAVER_CHANGED:
+ handleRestrictBackgroundChanged(toBool(msg.arg1));
+ break;
}
}
}
@@ -3783,6 +3893,8 @@
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
+ // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+ // necessary onBlockedStatusChanged callbacks.
mLockdownTracker = null;
if (existing != null) {
existing.shutdown();
@@ -4893,12 +5005,20 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
- // Report changes that are interesting for network statistics tracking.
if (prevNc != null) {
- final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
- newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ final boolean oldMetered = prevNc.isMetered();
+ final boolean newMetered = newNc.isMetered();
+ final boolean meteredChanged = oldMetered != newMetered;
+
+ if (meteredChanged) {
+ maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
+ mRestrictBackground);
+ }
+
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ // Report changes that are interesting for network statistics tracking.
if (meteredChanged || roamingChanged) {
notifyIfacesChangedForNetworkStats();
}
@@ -5028,6 +5148,8 @@
case ConnectivityManager.CALLBACK_AVAILABLE: {
putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ // For this notification, arg1 contains the blocked status.
+ msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_LOSING: {
@@ -5045,6 +5167,10 @@
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
break;
}
+ case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+ msg.arg1 = arg1;
+ break;
+ }
}
msg.what = notificationType;
msg.setData(bundle);
@@ -5600,7 +5726,76 @@
return;
}
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+ metered, mRestrictBackground);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+ }
+
+ /**
+ * Notify of the blocked state apps with a registered callback matching a given NAI.
+ *
+ * Unlike other callbacks, blocked status is different between each individual uid. So for
+ * any given nai, all requests need to be considered according to the uid who filed it.
+ *
+ * @param nai The target NetworkAgentInfo.
+ * @param oldMetered True if the previous network capabilities is metered.
+ * @param newRestrictBackground True if data saver is enabled.
+ */
+ private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
+ boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ final int uidRules = mUidRules.get(nri.mUid);
+ final boolean oldBlocked, newBlocked;
+ // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
+ // between these two calls.
+ synchronized (mVpns) {
+ oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
+ oldRestrictBackground);
+ newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
+ newRestrictBackground);
+ }
+ if (oldBlocked != newBlocked) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+ encodeBool(newBlocked));
+ }
+ }
+ }
+
+ /**
+ * Notify apps with a given UID of the new blocked state according to new uid rules.
+ * @param uid The uid for which the rules changed.
+ * @param newRules The new rules to apply.
+ */
+ private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean oldBlocked, newBlocked;
+ // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
+ // rules changed event. And this function actually loop through all connected nai and
+ // its requests. It seems that mVpns lock will be grabbed frequently in this case.
+ // Reduce the number of locking or optimize the use of lock are likely needed in future.
+ synchronized (mVpns) {
+ oldBlocked = isUidNetworkingWithVpnBlocked(
+ uid, mUidRules.get(uid), metered, mRestrictBackground);
+ newBlocked = isUidNetworkingWithVpnBlocked(
+ uid, newRules, metered, mRestrictBackground);
+ }
+ if (oldBlocked == newBlocked) {
+ return;
+ }
+ final int arg = encodeBool(newBlocked);
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ if (nri != null && nri.mUid == uid) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+ }
+ }
+ }
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index b2be5e6..793a177 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1270,26 +1270,36 @@
public int onCommand(String cmd) {
if ("vibrate".equals(cmd)) {
return runVibrate();
+ } else if ("prebaked".equals(cmd)) {
+ return runPrebaked();
}
return handleDefaultCommands(cmd);
}
+ private boolean checkDoNotDisturb() {
+ try {
+ final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ZEN_MODE);
+ if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ pw.print("Ignoring because device is on DND mode ");
+ pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+ zenMode));
+ return true;
+ }
+ }
+ } catch (SettingNotFoundException e) {
+ // ignore
+ }
+
+ return false;
+ }
+
private int runVibrate() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
try {
- try {
- final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ZEN_MODE);
- if (zenMode != Settings.Global.ZEN_MODE_OFF) {
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.print("Ignoring because device is on DND mode ");
- pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
- zenMode));
- return 0;
- }
- }
- } catch (SettingNotFoundException e) {
- // ignore
+ if (checkDoNotDisturb()) {
+ return 0;
}
final long duration = Long.parseLong(getNextArgRequired());
@@ -1311,6 +1321,30 @@
}
}
+ private int runPrebaked() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
+ try {
+ if (checkDoNotDisturb()) {
+ return 0;
+ }
+
+ final int id = Integer.parseInt(getNextArgRequired());
+
+ String description = getNextArg();
+ if (description == null) {
+ description = "Shell command";
+ }
+
+ VibrationEffect effect =
+ VibrationEffect.get(id, false);
+ vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
+ "Shell Command", mToken);
+ return 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
@@ -1321,6 +1355,9 @@
pw.println(" vibrate duration [description]");
pw.println(" Vibrates for duration milliseconds; ignored when device is on DND ");
pw.println(" (Do Not Disturb) mode.");
+ pw.println(" prebaked effect-id [description]");
+ pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
+ pw.println(" (Do Not Disturb) mode.");
pw.println("");
}
}
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index fcda83d..3939bee 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -339,7 +339,8 @@
Slog.w(TAG, uei.getSwitchStatePath() +
" not found while attempting to determine initial switch state");
} catch (Exception e) {
- Slog.e(TAG, "" , e);
+ Slog.e(TAG, "Error while attempting to determine initial switch state for "
+ + uei.getDevName() , e);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7235312..4070bca 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 @@
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 fecb573..ede13ef 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -37,16 +37,16 @@
import static com.android.server.am.ActivityDisplayProto.ID;
import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
import static com.android.server.am.ActivityDisplayProto.STACKS;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
+import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
+import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
-import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -873,6 +873,52 @@
return null;
}
+ ActivityRecord topRunningActivity() {
+ return topRunningActivity(false /* considerKeyguardState */);
+ }
+
+ /**
+ * Returns the top running activity in the focused stack. In the case the focused stack has no
+ * such activity, the next focusable stack on this display is returned.
+ *
+ * @param considerKeyguardState Indicates whether the locked state should be considered. if
+ * {@code true} and the keyguard is locked, only activities that
+ * can be shown on top of the keyguard will be considered.
+ * @return The top running activity. {@code null} if none is available.
+ */
+ ActivityRecord topRunningActivity(boolean considerKeyguardState) {
+ ActivityRecord topRunning = null;
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ topRunning = focusedStack.topRunningActivityLocked();
+ }
+
+ // Look in other focusable stacks.
+ if (topRunning == null) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ // Only consider focusable stacks other than the current focused one.
+ if (stack == focusedStack || !stack.isFocusable()) {
+ continue;
+ }
+ topRunning = stack.topRunningActivityLocked();
+ if (topRunning != null) {
+ break;
+ }
+ }
+ }
+
+ // This activity can be considered the top running activity if we are not considering
+ // the locked state, the keyguard isn't locked, or we can show when locked.
+ if (topRunning != null && considerKeyguardState
+ && mSupervisor.getKeyguardController().isKeyguardLocked()
+ && !topRunning.canShowWhenLocked()) {
+ return null;
+ }
+
+ return topRunning;
+ }
+
int getIndexOf(ActivityStack stack) {
return mStacks.indexOf(stack);
}
@@ -998,7 +1044,10 @@
* @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
*/
boolean supportsSystemDecorations() {
- return mDisplay.supportsSystemDecorations();
+ return mDisplay.supportsSystemDecorations()
+ // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
+ // (b/114338689) whenever vr 2d display id is set.
+ || mDisplayId == mSupervisor.mService.mVr2dDisplayId;
}
private boolean shouldDestroyContentOnRemove() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b1aea1f..d56b523 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_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_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_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.setProcessGroup;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
-import static android.os.Process.startWebView;
import static android.os.Process.zygoteProcess;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
@@ -193,6 +187,7 @@
import android.app.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.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.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.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.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.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 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 @@
// 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 @@
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
// Necessary ApplicationInfo flags to mark an app as persistent
- private static final int PERSISTENT_MASK =
+ static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
// Intent sent when remote bugreport collection has been completed
@@ -621,46 +617,12 @@
final ProcessList mProcessList = new ProcessList();
/**
- * All of the applications we currently have running organized by name.
- * The keys are strings of the application package name (as
- * returned by the package manager), and the keys are ApplicationRecord
- * objects.
- */
- final MyProcessMap mProcessNames = new MyProcessMap();
- final class MyProcessMap extends ProcessMap<ProcessRecord> {
- @Override
- public ProcessRecord put(String name, int uid, ProcessRecord value) {
- final ProcessRecord r = super.put(name, uid, value);
- mAtmInternal.onProcessAdded(r.getWindowProcessController());
- return r;
- }
-
- @Override
- public ProcessRecord remove(String name, int uid) {
- final ProcessRecord r = super.remove(name, uid);
- mAtmInternal.onProcessRemoved(name, uid);
- return r;
- }
- }
-
- /**
* Tracking long-term execution of processes to look for abuse and other
* bad app behavior.
*/
final ProcessStatsService mProcessStats;
/**
- * The currently running isolated processes.
- */
- final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
-
- /**
- * Counter for assigning isolated process uids, to avoid frequently reusing the
- * same ones.
- */
- int mNextIsolatedProcessUid = 0;
-
- /**
* Non-persistent appId whitelist for background restrictions
*/
int[] mBackgroundAppIdWhitelist = new int[] {
@@ -761,28 +723,6 @@
final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
/**
- * Processes that are being forcibly torn down.
- */
- final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
-
- /**
- * List of running applications, sorted by recent usage.
- * The first entry in the list is the least recently used.
- */
- final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
-
- /**
- * Where in mLruProcesses that the processes hosting activities start.
- */
- int mLruProcessActivityStart = 0;
-
- /**
- * Where in mLruProcesses that the processes hosting services start.
- * This is after (lower index) than mLruProcessesActivityStart.
- */
- int mLruProcessServiceStart = 0;
-
- /**
* List of processes that should gc as soon as things are idle.
*/
final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
@@ -1098,11 +1038,6 @@
int mAdjSeq = 0;
/**
- * Current sequence id for process LRU updating.
- */
- int mLruSeq = 0;
-
- /**
* Keep track of the non-cached/empty process we last found, to help
* determine how to distribute cached/empty processes next time.
*/
@@ -1214,31 +1149,6 @@
private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
- /**
- * A global counter for generating sequence numbers.
- * This value will be used when incrementing sequence numbers in individual uidRecords.
- *
- * Having a global counter ensures that seq numbers are monotonically increasing for a
- * particular uid even when the uidRecord is re-created.
- */
- @GuardedBy("this")
- @VisibleForTesting
- long mProcStateSeqCounter = 0;
-
- /**
- * A global counter for generating sequence numbers to uniquely identify pending process starts.
- */
- @GuardedBy("this")
- private long mProcStartSeqCounter = 0;
-
- /**
- * Contains {@link ProcessRecord} objects for pending process starts.
- *
- * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
- */
- @GuardedBy("this")
- private final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
-
private final Injector mInjector;
static final class ProcessChangeItem {
@@ -1426,9 +1336,6 @@
static final String SERVICE_RECORD_KEY = "servicerecord";
- static ServiceThread sKillThread = null;
- static KillHandler sKillHandler = null;
-
long mLastMemUsageReportTime = 0;
/**
@@ -1465,30 +1372,6 @@
return mActivityTaskManager.getGlobalConfiguration();
}
- final class KillHandler extends Handler {
- static final int KILL_PROCESS_GROUP_MSG = 4000;
-
- public KillHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case KILL_PROCESS_GROUP_MSG:
- {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
- Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- break;
-
- default:
- super.handleMessage(msg);
- }
- }
- }
-
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1602,8 +1485,8 @@
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mProcessList.mLruProcesses.get(i);
if (r.thread != null) {
try {
r.thread.updateTimeZone();
@@ -1616,16 +1499,7 @@
} break;
case CLEAR_DNS_CACHE_MSG: {
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
- try {
- r.thread.clearDnsCache();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
- }
- }
- }
+ mProcessList.clearAllDnsCacheLocked();
}
} break;
case UPDATE_HTTP_PROXY_MSG: {
@@ -1641,19 +1515,7 @@
pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- // Don't dispatch to isolated processes as they can't access
- // ConnectivityManager and don't have network privileges anyway.
- if (r.thread != null && !r.isolated) {
- try {
- r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to update http proxy for: " +
- r.info.processName);
- }
- }
- }
+ mProcessList.setAllHttpProxyLocked(host, port, exclList, pacFileUrl);
}
} break;
case PROC_START_TIMEOUT_MSG: {
@@ -1701,17 +1563,7 @@
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
- try {
- r.thread.updateTimePrefs(msg.arg1);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to update preferences for: "
- + r.info.processName);
- }
- }
- }
+ mProcessList.updateAllTimePrefsLocked(msg.arg1);
}
break;
}
@@ -1835,17 +1687,7 @@
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
- try {
- r.thread.handleTrustStorageUpdate();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to handle trust storage update for: " +
- r.info.processName);
- }
- }
- }
+ mProcessList.handleAllTrustStorageUpdateLocked();
}
} break;
}
@@ -1985,7 +1827,9 @@
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
- ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
+ ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
+ false,
+ 0);
app.setPersistent(true);
app.pid = MY_PID;
app.getWindowProcessController().setPid(MY_PID);
@@ -1994,7 +1838,7 @@
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
- updateLruProcessLocked(app, false, null);
+ mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
} catch (PackageManager.NameNotFoundException e) {
@@ -2262,24 +2106,38 @@
@VisibleForTesting
public ActivityManagerService(Injector injector) {
+ this(injector, null /* handlerThread */);
+ }
+
+ /**
+ * Provides the basic functionality for activity task related tests when a handler thread is
+ * given to initialize the dependency members.
+ */
+ @VisibleForTesting
+ ActivityManagerService(Injector injector, ServiceThread handlerThread) {
+ final boolean hasHandlerThread = handlerThread != null;
mInjector = injector;
mContext = mInjector.getContext();
mUiContext = null;
mAppErrors = null;
- mAppOpsService = mInjector.getAppOpsService(null, null);
+ mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
mBatteryStatsService = null;
- mConstants = null;
- mHandler = null;
- mHandlerThread = null;
- mIntentFirewall = null;
+ mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
+ mHandlerThread = handlerThread;
+ mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+ mIntentFirewall = hasHandlerThread
+ ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
mProcessCpuThread = null;
mProcessStats = null;
mProviderMap = null;
- mServices = null;
+ // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
+ // {@link ActivityStackSupervisor#cleanUpRemovedTaskLocked}.
+ mServices = hasHandlerThread ? new ActiveServices(this) : null;
mSystemThread = null;
- mUiHandler = injector.getUiHandler(null);
- mUserController = null;
- mPendingIntentController = null;
+ mUiHandler = injector.getUiHandler(null /* service */);
+ mUserController = hasHandlerThread ? new UserController(this) : null;
+ mPendingIntentController = hasHandlerThread
+ ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
@@ -2312,13 +2170,7 @@
mConstants = new ActivityManagerConstants(this, mHandler);
- /* static; one-time init here */
- if (sKillHandler == null) {
- sKillThread = new ServiceThread(TAG + ":kill",
- THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
- sKillThread.start();
- sKillHandler = new KillHandler(sKillThread.getLooper());
- }
+ mProcessList.init(this);
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
@@ -2468,12 +2320,13 @@
if (code == SYSPROPS_TRANSACTION) {
// We need to tell all apps about the system property change.
ArrayList<IBinder> procs = new ArrayList<IBinder>();
- synchronized(this) {
- final int NP = mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ synchronized (this) {
+ final int NP = mProcessList.mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ SparseArray<ProcessRecord> apps =
+ mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
+ for (int ia = 0; ia < NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
if (app.thread != null) {
procs.add(app.thread.asBinder());
@@ -2723,329 +2576,21 @@
mActivityTaskManager.unregisterTaskStackListener(listener);
}
- private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
- String what, Object obj, ProcessRecord srcApp) {
- app.lastActivityTime = now;
-
- if (app.hasActivitiesOrRecentTasks()) {
- // Don't want to touch dependent processes that are hosting activities.
- return index;
- }
-
- int lrui = mLruProcesses.lastIndexOf(app);
- if (lrui < 0) {
- Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
- + what + " " + obj + " from " + srcApp);
- return index;
- }
-
- if (lrui >= index) {
- // Don't want to cause this to move dependent processes *back* in the
- // list as if they were less frequently used.
- return index;
- }
-
- if (lrui >= mLruProcessActivityStart) {
- // Don't want to touch dependent processes that are hosting activities.
- return index;
- }
-
- mLruProcesses.remove(lrui);
- if (index > 0) {
- index--;
- }
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
- + " in LRU list: " + app);
- mLruProcesses.add(index, app);
- return index;
- }
-
- static void killProcessGroup(int uid, int pid) {
- if (sKillHandler != null) {
- sKillHandler.sendMessage(
- sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
- } else {
- Slog.w(TAG, "Asked to kill process group before system bringup!");
- Process.killProcessGroup(uid, pid);
- }
+ final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+ ProcessRecord client) {
+ mProcessList.updateLruProcessLocked(app, activityChange, client);
}
final void removeLruProcessLocked(ProcessRecord app) {
- int lrui = mLruProcesses.lastIndexOf(app);
- if (lrui >= 0) {
- if (!app.killed) {
- if (app.isPersistent()) {
- Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
- } else {
- Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
- if (app.pid > 0) {
- killProcessQuiet(app.pid);
- killProcessGroup(app.uid, app.pid);
- } else {
- app.pendingStart = false;
- }
- }
- }
- if (lrui <= mLruProcessActivityStart) {
- mLruProcessActivityStart--;
- }
- if (lrui <= mLruProcessServiceStart) {
- mLruProcessServiceStart--;
- }
- mLruProcesses.remove(lrui);
- }
- }
-
- final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
- ProcessRecord client) {
- final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
- || app.treatLikeActivity;
- final boolean hasService = false; // not impl yet. app.services.size() > 0;
- if (!activityChange && hasActivity) {
- // The process has activities, so we are only allowing activity-based adjustments
- // to move it. It should be kept in the front of the list with other
- // processes that have activities, and we don't want those to change their
- // order except due to activity operations.
- return;
- }
-
- mLruSeq++;
- final long now = SystemClock.uptimeMillis();
- app.lastActivityTime = now;
-
- // First a quick reject: if the app is already at the position we will
- // put it, then there is nothing to do.
- if (hasActivity) {
- final int N = mLruProcesses.size();
- if (N > 0 && mLruProcesses.get(N-1) == app) {
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
- return;
- }
- } else {
- if (mLruProcessServiceStart > 0
- && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
- return;
- }
- }
-
- int lrui = mLruProcesses.lastIndexOf(app);
-
- if (app.isPersistent() && lrui >= 0) {
- // We don't care about the position of persistent processes, as long as
- // they are in the list.
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
- return;
- }
-
- /* In progress: compute new position first, so we can avoid doing work
- if the process is not actually going to move. Not yet working.
- int addIndex;
- int nextIndex;
- boolean inActivity = false, inService = false;
- if (hasActivity) {
- // Process has activities, put it at the very tipsy-top.
- addIndex = mLruProcesses.size();
- nextIndex = mLruProcessServiceStart;
- inActivity = true;
- } else if (hasService) {
- // Process has services, put it at the top of the service list.
- addIndex = mLruProcessActivityStart;
- nextIndex = mLruProcessServiceStart;
- inActivity = true;
- inService = true;
- } else {
- // Process not otherwise of interest, it goes to the top of the non-service area.
- addIndex = mLruProcessServiceStart;
- if (client != null) {
- int clientIndex = mLruProcesses.lastIndexOf(client);
- if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
- + app);
- if (clientIndex >= 0 && addIndex > clientIndex) {
- addIndex = clientIndex;
- }
- }
- nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
- }
-
- Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
- + mLruProcessActivityStart + "): " + app);
- */
-
- if (lrui >= 0) {
- if (lrui < mLruProcessActivityStart) {
- mLruProcessActivityStart--;
- }
- if (lrui < mLruProcessServiceStart) {
- mLruProcessServiceStart--;
- }
- /*
- if (addIndex > lrui) {
- addIndex--;
- }
- if (nextIndex > lrui) {
- nextIndex--;
- }
- */
- mLruProcesses.remove(lrui);
- }
-
- /*
- mLruProcesses.add(addIndex, app);
- if (inActivity) {
- mLruProcessActivityStart++;
- }
- if (inService) {
- mLruProcessActivityStart++;
- }
- */
-
- int nextIndex;
- if (hasActivity) {
- final int N = mLruProcesses.size();
- if ((!app.hasActivities() || app.hasRecentTasks())
- && mLruProcessActivityStart < (N - 1)) {
- // Process doesn't have activities, but has clients with
- // activities... move it up, but one below the top (the top
- // should always have a real activity).
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Adding to second-top of LRU activity list: " + app);
- mLruProcesses.add(N - 1, app);
- // To keep it from spamming the LRU list (by making a bunch of clients),
- // we will push down any other entries owned by the app.
- final int uid = app.info.uid;
- for (int i = N - 2; i > mLruProcessActivityStart; i--) {
- ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid == uid) {
- // We want to push this one down the list. If the process after
- // it is for the same uid, however, don't do so, because we don't
- // want them internally to be re-ordered.
- if (mLruProcesses.get(i - 1).info.uid != uid) {
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Pushing uid " + uid + " swapping at " + i + ": "
- + mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
- ProcessRecord tmp = mLruProcesses.get(i);
- mLruProcesses.set(i, mLruProcesses.get(i - 1));
- mLruProcesses.set(i - 1, tmp);
- i--;
- }
- } else {
- // A gap, we can stop here.
- break;
- }
- }
- } else {
- // Process has activities, put it at the very tipsy-top.
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
- mLruProcesses.add(app);
- }
- nextIndex = mLruProcessServiceStart;
- } else if (hasService) {
- // Process has services, put it at the top of the service list.
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
- mLruProcesses.add(mLruProcessActivityStart, app);
- nextIndex = mLruProcessServiceStart;
- mLruProcessActivityStart++;
- } else {
- // Process not otherwise of interest, it goes to the top of the non-service area.
- int index = mLruProcessServiceStart;
- if (client != null) {
- // If there is a client, don't allow the process to be moved up higher
- // in the list than that client.
- int clientIndex = mLruProcesses.lastIndexOf(client);
- if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
- + " when updating " + app);
- if (clientIndex <= lrui) {
- // Don't allow the client index restriction to push it down farther in the
- // list than it already is.
- clientIndex = lrui;
- }
- if (clientIndex >= 0 && index > clientIndex) {
- index = clientIndex;
- }
- }
- if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
- mLruProcesses.add(index, app);
- nextIndex = index-1;
- mLruProcessActivityStart++;
- mLruProcessServiceStart++;
- }
-
- // If the app is currently using a content provider or service,
- // bump those processes as well.
- for (int j=app.connections.size()-1; j>=0; j--) {
- ConnectionRecord cr = app.connections.valueAt(j);
- if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
- && cr.binding.service.app != null
- && cr.binding.service.app.lruSeq != mLruSeq
- && !cr.binding.service.app.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
- "service connection", cr, app);
- }
- }
- for (int j=app.conProviders.size()-1; j>=0; j--) {
- ContentProviderRecord cpr = app.conProviders.get(j).provider;
- if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
- "provider reference", cpr, app);
- }
- }
+ mProcessList.removeLruProcessLocked(app);
}
final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
- if (uid == SYSTEM_UID) {
- // The system gets to run in any process. If there are multiple
- // processes with the same uid, just pick the first (this
- // should never happen).
- SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
- if (procs == null) return null;
- final int procCount = procs.size();
- for (int i = 0; i < procCount; i++) {
- final int procUid = procs.keyAt(i);
- if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
- // Don't use an app process or different user process for system component.
- continue;
- }
- return procs.valueAt(i);
- }
- }
- ProcessRecord proc = mProcessNames.get(processName, uid);
- if (false && proc != null && !keepIfLarge
- && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
- && proc.lastCachedPss >= 4000) {
- // Turn this condition on to cause killing to happen regularly, for testing.
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- proc.lastCachedPss, holder.appVersion);
- }
- }
- proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
- } else if (proc != null && !keepIfLarge
- && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
- && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
- if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
- for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
- StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
- proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- proc.lastCachedPss, holder.appVersion);
- }
- }
- proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
- }
- }
- return proc;
+ return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
+ }
+
+ final ProcessMap<ProcessRecord> getProcessNames() {
+ return mProcessList.mProcessNames;
}
void notifyPackageUse(String packageName, int reason) {
@@ -3072,7 +2617,7 @@
info.packageName = "android";
info.seInfoUser = SELinuxUtil.COMPLETE_STR;
info.targetSdkVersion = Build.VERSION.SDK_INT;
- ProcessRecord proc = startProcessLocked(processName, info /* info */,
+ ProcessRecord proc = mProcessList.startProcessLocked(processName, info /* info */,
false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */,
null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
@@ -3086,548 +2631,17 @@
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
- return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
+ return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
+ hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
- @GuardedBy("this")
- final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
- boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
- boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
- String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
- long startTime = SystemClock.elapsedRealtime();
- ProcessRecord app;
- if (!isolated) {
- app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
- checkTime(startTime, "startProcess: after getProcessRecord");
-
- if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
- // If we are in the background, then check to see if this process
- // is bad. If so, we will just silently fail.
- if (mAppErrors.isBadProcessLocked(info)) {
- if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
- + "/" + info.processName);
- return null;
- }
- } else {
- // When the user is explicitly starting a process, then clear its
- // crash count so that we won't make it bad until they see at
- // least one crash dialog again, and make the process good again
- // if it had been bad.
- if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
- + "/" + info.processName);
- mAppErrors.resetProcessCrashTimeLocked(info);
- if (mAppErrors.isBadProcessLocked(info)) {
- EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
- UserHandle.getUserId(info.uid), info.uid,
- info.processName);
- mAppErrors.clearBadProcessLocked(info);
- if (app != null) {
- app.bad = false;
- }
- }
- }
- } else {
- // If this is an isolated process, it can't re-use an existing process.
- app = null;
- }
-
- // We don't have to do anything more if:
- // (1) There is an existing application record; and
- // (2) The caller doesn't think it is dead, OR there is no thread
- // object attached to it so we know it couldn't have crashed; and
- // (3) There is a pid assigned to it, so it is either starting or
- // already running.
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
- + " app=" + app + " knownToBeDead=" + knownToBeDead
- + " thread=" + (app != null ? app.thread : null)
- + " pid=" + (app != null ? app.pid : -1));
- if (app != null && app.pid > 0) {
- if ((!knownToBeDead && !app.killed) || app.thread == null) {
- // We already have the app running, or are waiting for it to
- // come up (we have a pid but not yet its thread), so keep it.
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
- // If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, info.versionCode, mProcessStats);
- checkTime(startTime, "startProcess: done, added package to proc");
- return app;
- }
-
- // An application record is attached to a previous process,
- // clean it up now.
- if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
- checkTime(startTime, "startProcess: bad proc running, killing");
- killProcessGroup(app.uid, app.pid);
- handleAppDiedLocked(app, true, true);
- checkTime(startTime, "startProcess: done killing old proc");
- }
-
- String hostingNameStr = hostingName != null
- ? hostingName.flattenToShortString() : null;
-
- if (app == null) {
- checkTime(startTime, "startProcess: creating new process record");
- app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
- if (app == null) {
- Slog.w(TAG, "Failed making new process record for "
- + processName + "/" + info.uid + " isolated=" + isolated);
- return null;
- }
- app.crashHandler = crashHandler;
- app.isolatedEntryPoint = entryPoint;
- app.isolatedEntryPointArgs = entryPointArgs;
- checkTime(startTime, "startProcess: done creating new process record");
- } else {
- // If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, info.versionCode, mProcessStats);
- checkTime(startTime, "startProcess: added package to existing proc");
- }
-
- // If the system is not ready yet, then hold off on starting this
- // process until it is.
- if (!mProcessesReady
- && !isAllowedWhileBooting(info)
- && !allowWhileBooting) {
- if (!mProcessesOnHold.contains(app)) {
- mProcessesOnHold.add(app);
- }
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
- "System not ready, putting on hold: " + app);
- checkTime(startTime, "startProcess: returning with proc on hold");
- return app;
- }
-
- checkTime(startTime, "startProcess: stepping in to startProcess");
- final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
- checkTime(startTime, "startProcess: done starting proc!");
- return success ? app : null;
- }
-
boolean isAllowedWhileBooting(ApplicationInfo ai) {
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
- @GuardedBy("this")
- private final void startProcessLocked(ProcessRecord app,
- String hostingType, String hostingNameStr) {
- startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
- }
-
- @GuardedBy("this")
- private final boolean startProcessLocked(ProcessRecord app,
- String hostingType, String hostingNameStr, String abiOverride) {
- return startProcessLocked(app, hostingType, hostingNameStr,
- false /* disableHiddenApiChecks */, abiOverride);
- }
-
- /**
- * @return {@code true} if process start is successful, false otherwise.
- */
- @GuardedBy("this")
- private final boolean startProcessLocked(ProcessRecord app, String hostingType,
- String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
- if (app.pendingStart) {
- return true;
- }
- long startTime = SystemClock.elapsedRealtime();
- if (app.pid > 0 && app.pid != MY_PID) {
- checkTime(startTime, "startProcess: removing from pids map");
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(app.pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
- checkTime(startTime, "startProcess: done removing from pids map");
- app.setPid(0);
- }
-
- if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
- "startProcessLocked removing on hold: " + app);
- mProcessesOnHold.remove(app);
-
- checkTime(startTime, "startProcess: starting to update cpu stats");
- updateCpuStats();
- checkTime(startTime, "startProcess: done updating cpu stats");
-
- try {
- try {
- final int userId = UserHandle.getUserId(app.uid);
- AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
- int uid = app.uid;
- int[] gids = null;
- int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
- if (!app.isolated) {
- int[] permGids = null;
- try {
- checkTime(startTime, "startProcess: getting gids from package manager");
- final IPackageManager pm = AppGlobals.getPackageManager();
- permGids = pm.getPackageGids(app.info.packageName,
- MATCH_DEBUG_TRIAGED_MISSING, app.userId);
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
- app.info.packageName);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
- /*
- * Add shared application and profile GIDs so applications can share some
- * resources like shared libraries and access user-wide resources
- */
- if (ArrayUtils.isEmpty(permGids)) {
- gids = new int[3];
- } else {
- gids = new int[permGids.length + 3];
- System.arraycopy(permGids, 0, gids, 3, permGids.length);
- }
- gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
- gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
- gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
-
- // Replace any invalid GIDs
- if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
- if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
- }
- checkTime(startTime, "startProcess: building args");
- if (mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
- uid = 0;
- }
- int runtimeFlags = 0;
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
- runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
- // Also turn on CheckJNI for debuggable apps. It's quite
- // awkward to turn on otherwise.
- runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
- }
- // Run the app in safe mode if its manifest requests so or the
- // system is booted in safe mode.
- if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
- mSafeMode == true) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
- }
- if ("1".equals(SystemProperties.get("debug.checkjni"))) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
- }
- String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
- runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
- }
- String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
- if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
- runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
- }
- if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
- }
- if ("1".equals(SystemProperties.get("debug.assert"))) {
- runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
- }
- if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
- // Enable all debug flags required by the native debugger.
- runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
- runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
- runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
- mNativeDebuggingApp = null;
- }
-
- if (app.info.isPrivilegedApp() &&
- DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
- runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
- }
-
- if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
- app.info.maybeUpdateHiddenApiEnforcementPolicy(
- mHiddenApiBlacklist.getPolicyForPrePApps(),
- mHiddenApiBlacklist.getPolicyForPApps());
- @HiddenApiEnforcementPolicy int policy =
- app.info.getHiddenApiEnforcementPolicy();
- int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
- if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
- throw new IllegalStateException("Invalid API policy: " + policy);
- }
- runtimeFlags |= policyBits;
- }
-
- String invokeWith = null;
- if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- // Debuggable apps may include a wrapper script with their library directory.
- String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- try {
- if (new File(wrapperFileName).exists()) {
- invokeWith = "/system/bin/logwrapper " + wrapperFileName;
- }
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
-
- String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
- if (requiredAbi == null) {
- requiredAbi = Build.SUPPORTED_ABIS[0];
- }
-
- String instructionSet = null;
- if (app.info.primaryCpuAbi != null) {
- instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
- }
-
- app.gids = gids;
- app.setRequiredAbi(requiredAbi);
- app.instructionSet = instructionSet;
-
- // the per-user SELinux context must be set
- if (TextUtils.isEmpty(app.info.seInfoUser)) {
- Slog.wtf(TAG, "SELinux tag not defined",
- new IllegalStateException("SELinux tag not defined for "
- + app.info.packageName + " (uid " + app.uid + ")"));
- }
- final String seInfo = app.info.seInfo
- + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
- // Start the process. It will either succeed and return a result containing
- // the PID of the new process, or else throw a RuntimeException.
- final String entryPoint = "android.app.ActivityThread";
-
- return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
- runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
- startTime);
- } catch (RuntimeException e) {
- Slog.e(TAG, "Failure starting process " + app.processName, e);
-
- // Something went very wrong while trying to start this process; one
- // common case is when the package is frozen due to an active
- // upgrade. To recover, clean up any active bookkeeping related to
- // starting this process. (We already invoked this method once when
- // the package was initially frozen through KILL_APPLICATION_MSG, so
- // it doesn't hurt to use it again.)
- forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
- false, true, false, false, app.userId, "start failure");
- return false;
- }
- }
-
- @GuardedBy("this")
- private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
- ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
- String seInfo, String requiredAbi, String instructionSet, String invokeWith,
- long startTime) {
- app.pendingStart = true;
- app.killedByAm = false;
- app.removed = false;
- app.killed = false;
- final long startSeq = app.startSeq = ++mProcStartSeqCounter;
- app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
- if (mConstants.FLAG_PROCESS_START_ASYNC) {
- if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
- "Posting procStart msg for " + app.toShortString());
- mProcStartHandler.post(() -> {
- try {
- synchronized (ActivityManagerService.this) {
- final String reason = isProcStartValidLocked(app, startSeq);
- if (reason != null) {
- Slog.w(TAG_PROCESSES, app + " not valid anymore,"
- + " don't start process, " + reason);
- app.pendingStart = false;
- return;
- }
- app.setUsingWrapper(invokeWith != null
- || SystemProperties.get("wrap." + app.processName) != null);
- mPendingStarts.put(startSeq, app);
- }
- final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
- app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
- requiredAbi, instructionSet, invokeWith, app.startTime);
- synchronized (ActivityManagerService.this) {
- handleProcessStartedLocked(app, startResult, startSeq);
- }
- } catch (RuntimeException e) {
- synchronized (ActivityManagerService.this) {
- Slog.e(TAG, "Failure starting process " + app.processName, e);
- mPendingStarts.remove(startSeq);
- app.pendingStart = false;
- forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
- }
- }
- });
- return true;
- } else {
- try {
- final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
- uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
- invokeWith, startTime);
- handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
- startSeq, false);
- } catch (RuntimeException e) {
- Slog.e(TAG, "Failure starting process " + app.processName, e);
- app.pendingStart = false;
- forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
- }
- return app.pid > 0;
- }
- }
-
- private ProcessStartResult startProcess(String hostingType, String entryPoint,
- ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
- String seInfo, String requiredAbi, String instructionSet, String invokeWith,
- long startTime) {
- try {
- final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
- final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
- .getVisibleVolumesForUser(UserHandle.getUserId(uid));
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
- app.processName);
- checkTime(startTime, "startProcess: asking zygote to start proc");
- final ProcessStartResult startResult;
- if (hostingType.equals("webview_service")) {
- startResult = startWebView(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
- app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, null, app.info.packageName,
- packageNames, visibleVolIds,
- new String[] {PROC_START_SEQ_IDENT + app.startSeq});
- } else {
- startResult = Process.start(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
- app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, invokeWith, app.info.packageName,
- packageNames, visibleVolIds,
- new String[] {PROC_START_SEQ_IDENT + app.startSeq});
- }
- checkTime(startTime, "startProcess: returned from zygote!");
- return startResult;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
- }
-
- @GuardedBy("this")
- private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
- StringBuilder sb = null;
- if (app.killedByAm) {
- if (sb == null) sb = new StringBuilder();
- sb.append("killedByAm=true;");
- }
- if (mProcessNames.get(app.processName, app.uid) != app) {
- if (sb == null) sb = new StringBuilder();
- sb.append("No entry in mProcessNames;");
- }
- if (!app.pendingStart) {
- if (sb == null) sb = new StringBuilder();
- sb.append("pendingStart=false;");
- }
- if (app.startSeq > expectedStartSeq) {
- if (sb == null) sb = new StringBuilder();
- sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
- }
- return sb == null ? null : sb.toString();
- }
-
- @GuardedBy("this")
- private boolean handleProcessStartedLocked(ProcessRecord pending,
- ProcessStartResult startResult, long expectedStartSeq) {
- // Indicates that this process start has been taken care of.
- if (mPendingStarts.get(expectedStartSeq) == null) {
- if (pending.pid == startResult.pid) {
- pending.setUsingWrapper(startResult.usingWrapper);
- // TODO: Update already existing clients of usingWrapper
- }
- return false;
- }
- return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
- expectedStartSeq, false);
- }
-
- @GuardedBy("this")
- private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
- long expectedStartSeq, boolean procAttached) {
- mPendingStarts.remove(expectedStartSeq);
- final String reason = isProcStartValidLocked(app, expectedStartSeq);
- if (reason != null) {
- Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
- + ", " + reason);
- app.pendingStart = false;
- Process.killProcessQuiet(pid);
- Process.killProcessGroup(app.uid, app.pid);
- return false;
- }
- mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
- checkTime(app.startTime, "startProcess: done updating battery stats");
-
- EventLog.writeEvent(EventLogTags.AM_PROC_START,
- UserHandle.getUserId(app.startUid), pid, app.startUid,
- app.processName, app.hostingType,
- app.hostingNameStr != null ? app.hostingNameStr : "");
-
- try {
- AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
- app.seInfo, app.info.sourceDir, pid);
- } catch (RemoteException ex) {
- // Ignore
- }
-
- if (app.isPersistent()) {
- Watchdog.getInstance().processStarted(app.processName, pid);
- }
-
- checkTime(app.startTime, "startProcess: building log message");
- StringBuilder buf = mStringBuilder;
- buf.setLength(0);
- buf.append("Start proc ");
- buf.append(pid);
- buf.append(':');
- buf.append(app.processName);
- buf.append('/');
- UserHandle.formatUid(buf, app.startUid);
- if (app.isolatedEntryPoint != null) {
- buf.append(" [");
- buf.append(app.isolatedEntryPoint);
- buf.append("]");
- }
- buf.append(" for ");
- buf.append(app.hostingType);
- if (app.hostingNameStr != null) {
- buf.append(" ");
- buf.append(app.hostingNameStr);
- }
- reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
- app.setPid(pid);
- app.setUsingWrapper(usingWrapper);
- app.pendingStart = false;
- checkTime(app.startTime, "startProcess: starting to update pids map");
- ProcessRecord oldApp;
- synchronized (mPidsSelfLocked) {
- oldApp = mPidsSelfLocked.get(pid);
- }
- // If there is already an app occupying that pid that hasn't been cleaned up
- if (oldApp != null && !app.isolated) {
- // Clean up anything relating to this pid first
- Slog.w(TAG, "Reusing pid " + pid
- + " while app is still mapped to it");
- cleanUpApplicationRecordLocked(oldApp, false, false, -1,
- true /*replacingPid*/);
- }
- synchronized (mPidsSelfLocked) {
- this.mPidsSelfLocked.put(pid, app);
- if (!procAttached) {
- Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
- msg.obj = app;
- mHandler.sendMessageDelayed(msg, usingWrapper
- ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
- }
- }
- checkTime(app.startTime, "startProcess: done updating pids map");
- return true;
- }
-
void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + activity + "res=" + resumed);
@@ -3691,8 +2705,8 @@
int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
synchronized (this) {
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- final ProcessRecord proc = mLruProcesses.get(i);
+ for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+ final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
if (procState > proc.setProcState) {
if (proc.pkgList.containsKey(packageName) ||
(proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
@@ -3986,9 +3000,35 @@
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
- return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
- resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
- userId);
+ synchronized (this) {
+ /**
+ * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+ * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+ * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+ * later to modify process settings due to the flags.
+ * TODO(b/80414790): Investigate a better way of untangling this.
+ */
+ return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
+ resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+ bOptions, userId);
+ }
+ }
+
+ WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+ Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+ int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+ synchronized (this) {
+ /**
+ * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+ * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+ * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+ * later to modify process settings due to the flags.
+ * TODO(b/80414790): Investigate a better way of untangling this.
+ */
+ return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent,
+ resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+ bOptions, userId);
+ }
}
@Override
@@ -4067,7 +3107,7 @@
* to the process.
*/
@GuardedBy("this")
- private final void handleAppDiedLocked(ProcessRecord app,
+ final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
@@ -4092,32 +3132,19 @@
});
}
- private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
- final IBinder threadBinder = thread.asBinder();
- // Find the application record.
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- final ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
- return i;
- }
- }
- return -1;
- }
-
ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
if (thread == null) {
return null;
}
- int appIndex = getLRURecordIndexForAppLocked(thread);
- if (appIndex >= 0) {
- return mLruProcesses.get(appIndex);
- }
+ ProcessRecord record = mProcessList.getLRURecordForAppLocked(thread);
+ if (record != null) return record;
// Validation: if it isn't in the LRU list, it shouldn't exist, but let's
// double-check that.
final IBinder threadBinder = thread.asBinder();
- final ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+ final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+ mProcessList.mProcessNames.getMap();
for (int i = pmap.size()-1; i >= 0; i--) {
final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
for (int j = procs.size()-1; j >= 0; j--) {
@@ -4137,17 +3164,7 @@
// If there are no longer any background processes running,
// and the app that died was not running instrumentation,
// then tell everyone we are now low on memory.
- boolean haveBg = false;
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null
- && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
- haveBg = true;
- break;
- }
- }
-
- if (!haveBg) {
+ if (!mProcessList.haveBackgroundProcessLocked()) {
boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (doReport) {
long now = SystemClock.uptimeMillis();
@@ -4158,11 +3175,12 @@
}
}
final ArrayList<ProcessMemInfo> memInfos
- = doReport ? new ArrayList<ProcessMemInfo>(mLruProcesses.size()) : null;
- EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
+ = doReport ? new ArrayList<ProcessMemInfo>(mProcessList.getLruSizeLocked())
+ : null;
+ EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mProcessList.getLruSizeLocked());
long now = SystemClock.uptimeMillis();
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord rec = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord rec = mProcessList.mLruProcesses.get(i);
if (rec == dyingProc || rec.thread == null) {
continue;
}
@@ -4219,7 +3237,7 @@
if (!fromBinderDied) {
killProcessQuiet(pid);
}
- killProcessGroup(app.uid, pid);
+ ProcessList.killProcessGroup(app.uid, pid);
app.killed = true;
}
@@ -4638,7 +3656,7 @@
return;
}
synchronized (this) {
- killPackageProcessesLocked(packageName, appId, targetUserId,
+ mProcessList.killPackageProcessesLocked(packageName, appId, targetUserId,
ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
}
}
@@ -4661,30 +3679,7 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
- final ProcessRecord app = apps.valueAt(ia);
- if (app.isPersistent()) {
- // We don't kill persistent processes.
- continue;
- }
- if (app.removed) {
- procs.add(app);
- } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- app.removed = true;
- procs.add(app);
- }
- }
- }
-
- final int N = procs.size();
- for (int i = 0; i < N; i++) {
- removeProcessLocked(procs.get(i), false, true, "kill all background");
- }
+ mProcessList.killAllBackgroundProcessesLocked();
mAllowLowerMemLevel = true;
@@ -4718,27 +3713,7 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
- final ProcessRecord app = apps.valueAt(ia);
- if (app.removed) {
- procs.add(app);
- } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
- && (maxProcState < 0 || app.setProcState > maxProcState)) {
- app.removed = true;
- procs.add(app);
- }
- }
- }
-
- final int N = procs.size();
- for (int i = 0; i < N; i++) {
- removeProcessLocked(procs.get(i), false, true, "kill all background except");
- }
+ mProcessList.killAllBackgroundProcessesExceptLocked(minTargetSdk, maxProcState);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -5036,81 +4011,6 @@
null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
}
-
- @GuardedBy("this")
- private final boolean killPackageProcessesLocked(String packageName, int appId,
- int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
- boolean doit, boolean evenPersistent, String reason) {
- ArrayList<ProcessRecord> procs = new ArrayList<>();
-
- // Remove all processes this package may have touched: all with the
- // same UID (except for the system or root user), and all whose name
- // matches the package name.
- final int NP = mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
- ProcessRecord app = apps.valueAt(ia);
- if (app.isPersistent() && !evenPersistent) {
- // we don't kill persistent processes
- continue;
- }
- if (app.removed) {
- if (doit) {
- procs.add(app);
- }
- continue;
- }
-
- // Skip process if it doesn't meet our oom adj requirement.
- if (app.setAdj < minOomAdj) {
- continue;
- }
-
- // If no package is specified, we call all processes under the
- // give user id.
- if (packageName == null) {
- if (userId != UserHandle.USER_ALL && app.userId != userId) {
- continue;
- }
- if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
- continue;
- }
- // Package has been specified, we want to hit all processes
- // that match it. We need to qualify this by the processes
- // that are running under the specified app and user ID.
- } else {
- final boolean isDep = app.pkgDeps != null
- && app.pkgDeps.contains(packageName);
- if (!isDep && UserHandle.getAppId(app.uid) != appId) {
- continue;
- }
- if (userId != UserHandle.USER_ALL && app.userId != userId) {
- continue;
- }
- if (!app.pkgList.containsKey(packageName) && !isDep) {
- continue;
- }
- }
-
- // Process has passed all conditions, kill it!
- if (!doit) {
- return true;
- }
- app.removed = true;
- procs.add(app);
- }
- }
-
- int N = procs.size();
- for (int i=0; i<N; i++) {
- removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
- }
- updateOomAdjLocked();
- return N > 0;
- }
-
private void cleanupDisabledPackageComponentsLocked(
String packageName, int userId, boolean killProcess, String[] changedClasses) {
@@ -5231,7 +4131,7 @@
mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
}
- boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
+ boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
packageName == null ? ("stop user " + userId) : ("stop " + packageName));
@@ -5293,128 +4193,10 @@
return didSomething;
}
- private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
- return removeProcessNameLocked(name, uid, null);
- }
-
- private final ProcessRecord removeProcessNameLocked(final String name, final int uid,
- final ProcessRecord expecting) {
- ProcessRecord old = mProcessNames.get(name, uid);
- // Only actually remove when the currently recorded value matches the
- // record that we expected; if it doesn't match then we raced with a
- // newly created process and we don't want to destroy the new one.
- if ((expecting == null) || (old == expecting)) {
- mProcessNames.remove(name, uid);
- }
- if (old != null && old.uidRecord != null) {
- old.uidRecord.numProcs--;
- if (old.uidRecord.numProcs == 0) {
- // No more processes using this uid, tell clients it is gone.
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "No more processes in " + old.uidRecord);
- enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
- EventLogTags.writeAmUidStopped(uid);
- mActiveUids.remove(uid);
- noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
- }
- old.uidRecord = null;
- }
- mIsolatedProcesses.remove(uid);
- return old;
- }
-
- private final void addProcessNameLocked(ProcessRecord proc) {
- // We shouldn't already have a process under this name, but just in case we
- // need to clean up whatever may be there now.
- ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
- if (old == proc && proc.isPersistent()) {
- // We are re-adding a persistent process. Whatevs! Just leave it there.
- Slog.w(TAG, "Re-adding persistent process " + proc);
- } else if (old != null) {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
- }
- UidRecord uidRec = mActiveUids.get(proc.uid);
- if (uidRec == null) {
- uidRec = new UidRecord(proc.uid);
- // This is the first appearance of the uid, report it now!
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Creating new process uid: " + uidRec);
- if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
- || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
- uidRec.setWhitelist = uidRec.curWhitelist = true;
- }
- uidRec.updateHasInternetPermission();
- mActiveUids.put(proc.uid, uidRec);
- EventLogTags.writeAmUidRunning(uidRec.uid);
- noteUidProcessState(uidRec.uid, uidRec.curProcState);
- }
- proc.uidRecord = uidRec;
-
- // Reset render thread tid if it was already set, so new process can set it again.
- proc.renderThreadTid = 0;
- uidRec.numProcs++;
- mProcessNames.put(proc.processName, proc.uid, proc);
- if (proc.isolated) {
- mIsolatedProcesses.put(proc.uid, proc);
- }
- }
-
- @GuardedBy("this")
- boolean removeProcessLocked(ProcessRecord app,
- boolean callerWillRestart, boolean allowRestart, String reason) {
- final String name = app.processName;
- final int uid = app.uid;
- if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
- "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
-
- ProcessRecord old = mProcessNames.get(name, uid);
- if (old != app) {
- // This process is no longer active, so nothing to do.
- Slog.w(TAG, "Ignoring remove of inactive process: " + app);
- return false;
- }
- removeProcessNameLocked(name, uid);
- mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
-
- boolean needRestart = false;
- if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
- int pid = app.pid;
- if (pid > 0) {
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- }
- mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
- if (app.isolated) {
- mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
- getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
- }
- }
- boolean willRestart = false;
- if (app.isPersistent() && !app.isolated) {
- if (!callerWillRestart) {
- willRestart = true;
- } else {
- needRestart = true;
- }
- }
- app.kill(reason, true);
- handleAppDiedLocked(app, willRestart, allowRestart);
- if (willRestart) {
- removeLruProcessLocked(app);
- addAppLocked(app.info, null, false, null /* ABI override */);
- }
- } else {
- mRemovedProcesses.add(app);
- }
-
- return needRestart;
- }
-
@GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
- removeProcessLocked(app, false, true, "timeout publishing content providers");
+ mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
}
private final void processStartTimedOutLocked(ProcessRecord app) {
@@ -5432,7 +4214,7 @@
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
- removeProcessNameLocked(app.processName, app.uid);
+ mProcessList.removeProcessNameLocked(app.processName, app.uid);
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
@@ -5488,9 +4270,10 @@
// It's possible that process called attachApplication before we got a chance to
// update the internal state.
if (app == null && startSeq > 0) {
- final ProcessRecord pending = mPendingStarts.get(startSeq);
+ final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
if (pending != null && pending.startUid == callingUid
- && handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(),
+ && mProcessList.handleProcessStartedLocked(pending, pid, pending
+ .isUsingWrapper(),
startSeq, true)) {
app = pending;
}
@@ -5532,7 +4315,7 @@
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
- startProcessLocked(app, "link fail", processName);
+ mProcessList.startProcessLocked(app, "link fail", processName);
return false;
}
@@ -5743,7 +4526,7 @@
profilerInfo = null;
}
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
- updateLruProcessLocked(app, false, null);
+ mProcessList.updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
@@ -5754,7 +4537,7 @@
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- startProcessLocked(app, "bind fail", processName);
+ mProcessList.startProcessLocked(app, "bind fail", processName);
return false;
}
@@ -5927,7 +4710,7 @@
for (int ip=0; ip<NP; ip++) {
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
+ procs.get(ip));
- startProcessLocked(procs.get(ip), "on-hold", null);
+ mProcessList.startProcessLocked(procs.get(ip), "on-hold", null);
}
}
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -6884,19 +5667,7 @@
@Override
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
- final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
- final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
- outInfo.availMem = getFreeMemory();
- outInfo.totalMem = getTotalMemory();
- outInfo.threshold = homeAppMem;
- outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
- outInfo.hiddenAppThreshold = cachedAppMem;
- outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
- ProcessList.SERVICE_ADJ);
- outInfo.visibleAppThreshold = mProcessList.getMemLevel(
- ProcessList.VISIBLE_APP_ADJ);
- outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
- ProcessList.FOREGROUND_APP_ADJ);
+ mProcessList.getMemoryInfo(outInfo);
}
// =========================================================
@@ -7332,7 +6103,7 @@
return false;
}
- private void checkTime(long startTime, String where) {
+ void checkTime(long startTime, String where) {
long now = SystemClock.uptimeMillis();
if ((now-startTime) > 50) {
// If we are taking more than 50ms, log about it.
@@ -7374,6 +6145,7 @@
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
+ boolean providerRunning = false;
synchronized(this) {
long startTime = SystemClock.uptimeMillis();
@@ -7413,8 +6185,6 @@
}
}
- boolean providerRunning = false;
-
if (cpr != null && cpr.proc != null) {
providerRunning = !cpr.proc.killed;
@@ -7492,7 +6262,7 @@
// back up on the LRU list. This is good because
// content providers are often expensive to start.
checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
- updateLruProcessLocked(cpr.proc, false, null);
+ mProcessList.updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
@@ -7739,6 +6509,7 @@
}
// Wait for the provider to be published...
+ final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
@@ -7753,13 +6524,22 @@
return null;
}
try {
+ final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
if (DEBUG_MU) Slog.v(TAG_MU,
"Waiting to start provider " + cpr
- + " launchingApp=" + cpr.launchingApp);
+ + " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");
if (conn != null) {
conn.waiting = true;
}
- cpr.wait();
+ cpr.wait(wait);
+ if (cpr.provider == null) {
+ Slog.wtf(TAG, "Timeout waiting for provider "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name
+ + " providerRunning=" + providerRunning);
+ return null;
+ }
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
@@ -8128,7 +6908,7 @@
public final void installSystemProviders() {
List<ProviderInfo> providers;
synchronized (this) {
- ProcessRecord app = mProcessNames.get("system", SYSTEM_UID);
+ ProcessRecord app = mProcessList.mProcessNames.get("system", SYSTEM_UID);
providers = generateApplicationProvidersLocked(app);
if (providers != null) {
for (int i=providers.size()-1; i>=0; i--) {
@@ -8190,9 +6970,10 @@
final int matchFlags = GET_PROVIDERS | MATCH_DIRECT_BOOT_UNAWARE;
synchronized (this) {
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final SparseArray<ProcessRecord> apps = mProcessList.mProcessNames.getMap().valueAt
+ (ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
@@ -8298,67 +7079,6 @@
// GLOBAL MANAGEMENT
// =========================================================
- @GuardedBy("this")
- final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
- boolean isolated, int isolatedUid) {
- String proc = customProcess != null ? customProcess : info.processName;
- final int userId = UserHandle.getUserId(info.uid);
- int uid = info.uid;
- if (isolated) {
- if (isolatedUid == 0) {
- int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
- while (true) {
- if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
- || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
- mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
- }
- uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
- mNextIsolatedProcessUid++;
- if (mIsolatedProcesses.indexOfKey(uid) < 0) {
- // No process for this uid, use it.
- break;
- }
- stepsLeft--;
- if (stepsLeft <= 0) {
- return null;
- }
- }
- } else {
- // Special case for startIsolatedProcess (internal only), where
- // the uid of the isolated process is specified by the caller.
- uid = isolatedUid;
- }
- getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
-
- // Register the isolated UID with this application so BatteryStats knows to
- // attribute resource usage to the application.
- //
- // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
- // about the process state of the isolated UID *before* it is registered with the
- // owning application.
- mBatteryStatsService.addIsolatedUid(uid, info.uid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
- StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
- }
- final ProcessRecord r = new ProcessRecord(this, info, proc, uid, getGlobalConfiguration());
- if (!mBooted && !mBooting
- && userId == UserHandle.USER_SYSTEM
- && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
- // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
- r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
- r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- r.setPersistent(true);
- r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
- }
- if (isolated && isolatedUid != 0) {
- // Special case for startIsolatedProcess (internal only) - assume the process
- // is required by the system server to prevent it being killed.
- r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
- }
- addProcessNameLocked(r);
- return r;
- }
-
private boolean uidOnBackgroundWhitelist(final int uid) {
final int appId = UserHandle.getAppId(uid);
final int[] whitelist = mBackgroundAppIdWhitelist;
@@ -8419,6 +7139,7 @@
abiOverride);
}
+ // TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, String abiOverride) {
@@ -8431,8 +7152,8 @@
}
if (app == null) {
- app = newProcessRecordLocked(info, customProcess, isolated, 0);
- updateLruProcessLocked(app, false, null);
+ app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+ mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
@@ -8452,7 +7173,7 @@
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
- startProcessLocked(app, "added application",
+ mProcessList.startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
abiOverride);
}
@@ -9316,7 +8037,7 @@
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
- killPackageProcessesLocked(null, appId, userId,
+ mProcessList.killPackageProcessesLocked(null, appId, userId,
ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true,
reason != null ? reason : "kill uid");
} finally {
@@ -9466,8 +8187,8 @@
final long memoryGrowthThreshold =
Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord proc = mProcessList.mLruProcesses.get(i);
if (proc.notCachedSinceIdle) {
if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
@@ -9597,7 +8318,7 @@
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
Slog.i(TAG, "Removing system update proc: " + proc);
- removeProcessLocked(proc, true, false, "system update done");
+ mProcessList.removeProcessLocked(proc, true, false, "system update done");
}
}
@@ -9984,22 +8705,7 @@
}
synchronized (this) {
- final int NP = mProcessNames.getMap().size();
- for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia=0; ia<NA; ia++) {
- ProcessRecord p = apps.valueAt(ia);
- if (p.thread != null && p.thread.asBinder() == app) {
- return p;
- }
- }
- }
-
- Slog.w(TAG, "Can't find mystery application for " + reason
- + " from pid=" + Binder.getCallingPid()
- + " uid=" + Binder.getCallingUid() + ": " + app);
- return null;
+ return mProcessList.findAppProcessLocked(app, reason);
}
}
@@ -10215,8 +8921,8 @@
synchronized (this) {
// iterate across all processes
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!allUsers && app.userId != userId) {
continue;
}
@@ -10249,44 +8955,6 @@
return errList;
}
- static int procStateToImportance(int procState, int memAdj,
- ActivityManager.RunningAppProcessInfo currApp,
- int clientTargetSdk) {
- int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
- procState, clientTargetSdk);
- if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- currApp.lru = memAdj;
- } else {
- currApp.lru = 0;
- }
- return imp;
- }
-
- @GuardedBy("this")
- private void fillInProcMemInfoLocked(ProcessRecord app,
- ActivityManager.RunningAppProcessInfo outInfo,
- int clientTargetSdk) {
- outInfo.pid = app.pid;
- outInfo.uid = app.info.uid;
- if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
- outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
- }
- if (app.isPersistent()) {
- outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
- }
- if (app.hasActivities()) {
- outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
- }
- outInfo.lastTrimLevel = app.trimMemoryLevel;
- int adj = app.curAdj;
- int procState = app.getCurProcState();
- outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
- outInfo.importanceReasonCode = app.adjTypeCode;
- outInfo.processState = app.getCurProcState();
- outInfo.isFocused = (app == getTopAppLocked());
- outInfo.lastActivityTime = app.lastActivityTime;
- }
-
@Override
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
@@ -10294,8 +8962,6 @@
final int callingUid = Binder.getCallingUid();
final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
- // Lazy instantiation of list
- List<ActivityManager.RunningAppProcessInfo> runList = null;
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
callingUid) == PackageManager.PERMISSION_GRANTED;
final int userId = UserHandle.getUserId(callingUid);
@@ -10304,44 +8970,9 @@
synchronized (this) {
// Iterate across all processes
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
- if ((!allUsers && app.userId != userId)
- || (!allUids && app.uid != callingUid)) {
- continue;
- }
- if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
- // Generate process state info for running application
- ActivityManager.RunningAppProcessInfo currApp =
- new ActivityManager.RunningAppProcessInfo(app.processName,
- app.pid, app.getPackageList());
- fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
- if (app.adjSource instanceof ProcessRecord) {
- currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
- currApp.importanceReasonImportance =
- ActivityManager.RunningAppProcessInfo.procStateToImportance(
- app.adjSourceProcState);
- } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
- ActivityServiceConnectionsHolder r =
- (ActivityServiceConnectionsHolder) app.adjSource;
- final int pid = r.getActivityPid();
- if (pid != -1) {
- currApp.importanceReasonPid = pid;
- }
- }
- if (app.adjTarget instanceof ComponentName) {
- currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
- }
- //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
- // + " lru=" + currApp.lru);
- if (runList == null) {
- runList = new ArrayList<>();
- }
- runList.add(currApp);
- }
- }
+ return mProcessList.getRunningAppProcessesLocked(allUsers, userId, allUids,
+ callingUid, clientTargetSdk);
}
- return runList;
}
@Override
@@ -10388,7 +9019,7 @@
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
- fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
+ mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
}
}
}
@@ -11005,6 +9636,7 @@
"Counts of Binder Proxies held by SYSTEM");
}
+ // TODO: Move to ProcessList?
@GuardedBy("this")
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
@@ -11014,9 +9646,9 @@
pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
if (dumpAll) {
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord r = procs.valueAt(ia);
@@ -11038,10 +9670,10 @@
}
}
- if (mIsolatedProcesses.size() > 0) {
+ if (mProcessList.mIsolatedProcesses.size() > 0) {
boolean printed = false;
- for (int i=0; i<mIsolatedProcesses.size(); i++) {
- ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+ ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
@@ -11094,17 +9726,13 @@
}
}
- if (mLruProcesses.size() > 0) {
+ if (mProcessList.getLruSizeLocked() > 0) {
if (needSep) {
pw.println();
}
- pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
- pw.print(" total, non-act at ");
- pw.print(mLruProcesses.size()-mLruProcessActivityStart);
- pw.print(", non-svc at ");
- pw.print(mLruProcesses.size()-mLruProcessServiceStart);
- pw.println("):");
- dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", false, dumpPackage);
+ mProcessList.dumpLruListHeaderLocked(pw);
+ dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", false,
+ dumpPackage);
needSep = true;
}
@@ -11158,11 +9786,11 @@
"Starting Norm", "Restarting PERS", dumpPackage);
}
- if (mRemovedProcesses.size() > 0) {
+ if (mProcessList.mRemovedProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" Processes that are being removed:");
- dumpProcessList(pw, this, mRemovedProcesses, " ",
+ dumpProcessList(pw, this, mProcessList.mRemovedProcesses, " ",
"Removed Norm", "Removed PERS", dumpPackage);
}
@@ -11187,12 +9815,13 @@
needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
mTestPssMode, mWakefulness);
- if (dumpAll && mPendingStarts.size() > 0) {
+ if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" mPendingStarts: ");
- for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
- pw.println(" " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
+ for (int i = 0, len = mProcessList.mPendingStarts.size(); i < len; ++i ) {
+ pw.println(" " + mProcessList.mPendingStarts.keyAt(i) + ": "
+ + mProcessList.mPendingStarts.valueAt(i));
}
}
if (dumpAll) {
@@ -11345,9 +9974,9 @@
pw.print(" mLastPowerCheckUptime=");
TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
pw.println("");
- pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
+ pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs
- + " (" + mLruProcesses.size() + " total)"
+ + " (" + mProcessList.getLruSizeLocked() + " total)"
+ " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+ " mNumServiceProcs=" + mNumServiceProcs
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
@@ -11393,9 +10022,9 @@
void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip=0; ip<NP; ip++) {
- SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+ SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia = 0; ia<NA; ia++) {
ProcessRecord r = procs.valueAt(ia);
@@ -11409,8 +10038,8 @@
}
}
- for (int i=0; i<mIsolatedProcesses.size(); i++) {
- ProcessRecord r = mIsolatedProcesses.valueAt(i);
+ for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+ ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
continue;
}
@@ -11443,14 +10072,17 @@
uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
}
- if (mLruProcesses.size() > 0) {
+ if (mProcessList.getLruSizeLocked() > 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
- int total = mLruProcesses.size();
+ int total = mProcessList.getLruSizeLocked();
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
- proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
- proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
- writeProcessOomListToProto(proto, ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
- mLruProcesses,false, dumpPackage);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
+ total - mProcessList.mLruProcessActivityStart);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
+ total - mProcessList.mLruProcessServiceStart);
+ writeProcessOomListToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
+ mProcessList.mLruProcesses,false, dumpPackage);
proto.end(lruToken);
}
@@ -11461,7 +10093,8 @@
if (!r.pkgList.containsKey(dumpPackage)) {
continue;
}
- r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
+ r.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
}
}
}
@@ -11475,7 +10108,8 @@
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
- it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
+ it.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
}
}
}
@@ -11485,11 +10119,12 @@
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
- r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
+ r.writeToProto(proto,
+ ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
}
- for (int i=0; i<mRemovedProcesses.size(); i++) {
- ProcessRecord r = mRemovedProcesses.get(i);
+ for (int i = 0; i < mProcessList.mRemovedProcesses.size(); i++) {
+ ProcessRecord r = mProcessList.mRemovedProcesses.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
@@ -11623,7 +10258,7 @@
proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
- proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mLruSeq);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.mLruSeq);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
@@ -11704,7 +10339,7 @@
int opti, boolean dumpAll) {
boolean needSep = false;
- if (mLruProcesses.size() > 0) {
+ if (mProcessList.getLruSizeLocked() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" OOM levels:");
@@ -11724,13 +10359,16 @@
printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
if (needSep) pw.println();
- pw.print(" Process OOM control ("); pw.print(mLruProcesses.size());
+ pw.print(" Process OOM control ("); pw.print(mProcessList.getLruSizeLocked());
pw.print(" total, non-act at ");
- pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+ pw.print(mProcessList.getLruSizeLocked()
+ - mProcessList.mLruProcessActivityStart);
pw.print(", non-svc at ");
- pw.print(mLruProcesses.size()-mLruProcessServiceStart);
+ pw.print(mProcessList.getLruSizeLocked()
+ - mProcessList.mLruProcessServiceStart);
pw.println("):");
- dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", true, null);
+ dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", true,
+ null);
needSep = true;
}
@@ -12365,35 +11003,9 @@
ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
String[] args) {
- ArrayList<ProcessRecord> procs;
synchronized (this) {
- if (args != null && args.length > start
- && args[start].charAt(0) != '-') {
- procs = new ArrayList<ProcessRecord>();
- int pid = -1;
- try {
- pid = Integer.parseInt(args[start]);
- } catch (NumberFormatException e) {
- }
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord proc = mLruProcesses.get(i);
- if (proc.pid > 0 && proc.pid == pid) {
- procs.add(proc);
- } else if (allPkgs && proc.pkgList != null
- && proc.pkgList.containsKey(args[start])) {
- procs.add(proc);
- } else if (proc.processName.equals(args[start])) {
- procs.add(proc);
- }
- }
- if (procs.size() <= 0) {
- return null;
- }
- } else {
- procs = new ArrayList<ProcessRecord>(mLruProcesses);
- }
+ return mProcessList.collectProcessesLocked(start, allPkgs, args);
}
- return procs;
}
final void dumpGraphicsHardwareUsage(FileDescriptor fd,
@@ -14088,7 +12700,7 @@
* app that was passed in must remain on the process lists.
*/
@GuardedBy("this")
- private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
+ final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
if (index >= 0) {
removeLruProcessLocked(app);
@@ -14228,7 +12840,7 @@
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
if (!replacingPid) {
- removeProcessNameLocked(app.processName, app.uid, app);
+ mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
}
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
} else if (!app.removed) {
@@ -14252,9 +12864,9 @@
if (index < 0) {
ProcessList.remove(app.pid);
}
- addProcessNameLocked(app);
+ mProcessList.addProcessNameLocked(app);
app.pendingStart = false;
- startProcessLocked(app, "restart", app.processName);
+ mProcessList.startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
@@ -14991,15 +13603,7 @@
}
private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
- try {
- r.thread.dispatchPackageBroadcast(cmd, packages);
- } catch (RemoteException ex) {
- }
- }
- }
+ mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
}
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
@@ -15408,8 +14012,10 @@
}
} else {
if (killProcess) {
- killPackageProcessesLocked(ssp, UserHandle.getAppId(
- intent.getIntExtra(Intent.EXTRA_UID, -1)),
+ final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
+ -1);
+ mProcessList.killPackageProcessesLocked(ssp,
+ UserHandle.getAppId(extraUid),
userId, ProcessList.INVALID_ADJ,
false, true, true, false, "change " + ssp);
}
@@ -16398,7 +15004,7 @@
}
}
- private void noteUidProcessState(final int uid, final int state) {
+ void noteUidProcessState(final int uid, final int state) {
mBatteryStatsService.noteUidProcessState(uid, state);
mAppOpsService.updateUidProcState(uid, state);
if (mTrackingAssociations) {
@@ -16430,6 +15036,7 @@
private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
+ /** These methods are called inline during computeOomAdjLocked(), on the same thread */
private final class ComputeOomAdjWindowCallback
implements WindowProcessController.ComputeOomAdjCallback {
@@ -17611,10 +16218,10 @@
for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
}
- mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
+ mPendingPssProcesses.ensureCapacity(mProcessList.getLruSizeLocked());
mPendingPssProcesses.clear();
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ for (int i = mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (app.thread == null
|| app.getCurProcState() == ActivityManager.PROCESS_STATE_NONEXISTENT) {
continue;
@@ -17790,10 +16397,10 @@
final long curUptime = SystemClock.uptimeMillis();
final long uptimeSince = curUptime - mLastPowerCheckUptime;
mLastPowerCheckUptime = curUptime;
- int i = mLruProcesses.size();
+ int i = mProcessList.mLruProcesses.size();
while (i > 0) {
i--;
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
if (app.lastCpuTime <= 0) {
continue;
@@ -18343,7 +16950,7 @@
// TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
// the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
- private ProcessRecord getTopAppLocked() {
+ ProcessRecord getTopAppLocked() {
final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null;
final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null;
String pkg;
@@ -18417,7 +17024,7 @@
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
- final int N = mLruProcesses.size();
+ final int N = mProcessList.getLruSizeLocked();
// Reset state in all uid records.
for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -18478,11 +17085,11 @@
// need to reset cycle state before calling computeOomAdjLocked because of service connections
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
app.containsCycle = false;
}
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
@@ -18557,7 +17164,7 @@
retryCycles = false;
for (int i=0; i<N; i++) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
app.adjSeq--;
app.completedAdjSeq--;
@@ -18565,7 +17172,7 @@
}
for (int i=0; i<N; i++) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
@@ -18576,7 +17183,7 @@
}
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
applyOomAdjLocked(app, true, now, nowElapsed);
@@ -18669,9 +17276,9 @@
// has gone down since last time.
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
- + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);
+ + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
if (memFactor > mLastMemoryLevel) {
- if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {
+ if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
memFactor = mLastMemoryLevel;
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
}
@@ -18681,7 +17288,7 @@
StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
}
mLastMemoryLevel = memFactor;
- mLastNumProcesses = mLruProcesses.size();
+ mLastNumProcesses = mProcessList.getLruSizeLocked();
boolean allChanged = mProcessStats.setMemFactorLocked(
memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
final int trackerMemFactor = mProcessStats.getMemFactorLocked();
@@ -18709,7 +17316,7 @@
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
@@ -18787,7 +17394,7 @@
mLowRamStartTime = 0;
}
for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
@@ -19051,7 +17658,7 @@
continue;
}
synchronized (uidRec.networkStateLock) {
- uidRec.curProcStateSeq = ++mProcStateSeqCounter;
+ uidRec.curProcStateSeq = ++mProcessList.mProcStateSeqCounter; // TODO: use method
if (blockState == NETWORK_STATE_BLOCK) {
if (blockingUids == null) {
blockingUids = new ArrayList<>();
@@ -19074,8 +17681,8 @@
return;
}
- for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
- final ProcessRecord app = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; --i) {
+ final ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (!blockingUids.contains(app.uid)) {
continue;
}
@@ -19273,8 +17880,8 @@
final void trimApplicationsLocked() {
// First remove any unused application processes whose package
// has been removed.
- for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
- final ProcessRecord app = mRemovedProcesses.get(i);
+ for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
if (!app.hasActivitiesOrRecentTasks()
&& app.curReceivers.isEmpty() && app.services.size() == 0) {
Slog.i(
@@ -19292,7 +17899,7 @@
}
}
cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
- mRemovedProcesses.remove(i);
+ mProcessList.mRemovedProcesses.remove(i);
if (app.isPersistent()) {
addAppLocked(app.info, null, false, null /* ABI override */);
@@ -19318,8 +17925,8 @@
+ android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
}
- for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mLruProcesses.get(i);
+ for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = mProcessList.mLruProcesses.get(i);
if (r.thread != null && r.isPersistent()) {
sendSignal(r.pid, sig);
}
@@ -19446,7 +18053,7 @@
if (proc == null) {
ArrayMap<String, SparseArray<ProcessRecord>> all
- = mProcessNames.getMap();
+ = mProcessList.mProcessNames.getMap();
SparseArray<ProcessRecord> procs = all.get(process);
if (procs != null && procs.size() > 0) {
proc = procs.valueAt(0);
@@ -19576,15 +18183,8 @@
}
void onCoreSettingsChange(Bundle settings) {
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord processRecord = mLruProcesses.get(i);
- try {
- if (processRecord.thread != null) {
- processRecord.thread.setCoreSettings(settings);
- }
- } catch (RemoteException re) {
- /* ignore */
- }
+ synchronized (this) {
+ mProcessList.updateCoreSettingsLocked(settings);
}
}
@@ -19716,8 +18316,8 @@
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- for (int i = 0; i < mLruProcesses.size(); i++) {
- ProcessRecord process = mLruProcesses.get(i);
+ for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+ ProcessRecord process = mProcessList.mLruProcesses.get(i);
if (!processSanityChecksLocked(process)) {
continue;
}
@@ -19749,7 +18349,7 @@
PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
pw.println("Binder transaction traces for all processes.\n");
- for (ProcessRecord process : mLruProcesses) {
+ for (ProcessRecord process : mProcessList.mLruProcesses) {
if (!processSanityChecksLocked(process)) {
continue;
}
@@ -19810,9 +18410,10 @@
public void killForegroundAppsForUser(int userHandle) {
synchronized (ActivityManagerService.this) {
final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessNames.getMap().size();
+ final int NP = mProcessList.mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
- final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final SparseArray<ProcessRecord> apps =
+ mProcessList.mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
@@ -19831,7 +18432,7 @@
final int N = procs.size();
for (int i = 0; i < N; i++) {
- removeProcessLocked(procs.get(i), false, true, "kill all fg");
+ mProcessList.removeProcessLocked(procs.get(i), false, true, "kill all fg");
}
}
}
@@ -20089,8 +18690,8 @@
if (packageName == null) return false;
synchronized (ActivityManagerService.this) {
- for (int i = 0; i < mLruProcesses.size(); i++) {
- final ProcessRecord pr = mLruProcesses.get(i);
+ for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+ final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
if (pr.uid != uid) {
continue;
}
@@ -20323,6 +18924,110 @@
(WindowProcessController) parentProc, aboveSystem, reason);
}
+
+ @Override
+ public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
+ synchronized (ActivityManagerService.this) {
+ Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+ intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ if (initLocale || !mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+
+ // Send a broadcast to PackageInstallers if the configuration change is interesting
+ // for the purposes of installing additional splits.
+ if (!initLocale && isSplitConfigurationChange(changes)) {
+ intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+ // Typically only app stores will have this permission.
+ String[] permissions =
+ new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
+ permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this configuration change is interesting enough to send an
+ * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+ */
+ private boolean isSplitConfigurationChange(int configDiff) {
+ return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+ }
+
+ @Override
+ public void broadcastCloseSystemDialogs(String reason) {
+ synchronized (ActivityManagerService.this) {
+ final Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ if (reason != null) {
+ intent.putExtra("reason", reason);
+ }
+
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+ }
+
+ @Override
+ public void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.killAllBackgroundProcessesExcept(
+ minTargetSdk, maxProcState);
+ }
+ }
+
+ @Override
+ public void startProcess(String processName, ApplicationInfo info,
+ boolean knownToBeDead, String hostingType, ComponentName hostingName) {
+ synchronized (ActivityManagerService.this) {
+ startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
+ hostingType, hostingName, false /* allowWhileBooting */,
+ false /* isolated */, true /* keepIfLarge */);
+ }
+ }
+
+ @Override
+ public void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+ ProfilerInfo profilerInfo) {
+ synchronized (ActivityManagerService.this) {
+ if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
+ setDebugApp(aInfo.processName, true, false);
+ }
+
+ if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
+ setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+ }
+
+ if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
+ setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
+ }
+
+ if (profilerInfo != null) {
+ setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+ }
+ }
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -20504,8 +19209,8 @@
}
try {
synchronized(this) {
- killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid), userId,
- ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
+ mProcessList.killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid),
+ userId, ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
"dep: " + packageName);
}
} finally {
@@ -20535,33 +19240,9 @@
void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
final boolean updateFrameworkRes = packagesToUpdate.contains("android");
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- final ProcessRecord app = mLruProcesses.get(i);
- if (app.thread == null) {
- continue;
- }
- if (userId != UserHandle.USER_ALL && app.userId != userId) {
- continue;
- }
+ mProcessList.updateApplicationInfoLocked(packagesToUpdate, userId, updateFrameworkRes);
- final int packageCount = app.pkgList.size();
- for (int j = 0; j < packageCount; j++) {
- final String packageName = app.pkgList.keyAt(j);
- if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
- try {
- final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
- if (ai != null) {
- app.thread.scheduleApplicationInfoChanged(ai);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
- packageName, app));
- }
- }
- }
- }
if (updateFrameworkRes) {
// Update system server components that need to know about changed overlays. Because the
// overlay is applied in ActivityThread, we need to serialize through its thread too.
@@ -20583,7 +19264,8 @@
public 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 9f768a8..692b2d4 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 @@
options.setLockTaskEnabled(true);
}
if (mWaitOption) {
- result = mTaskInterface.startActivityAndWait(null, null, intent, mimeType,
+ result = mInternal.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
res = result.result;
} else {
- res = mTaskInterface.startActivityAsUser(null, null, intent, mimeType,
+ res = mInternal.startActivityAsUser(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 12ed726..026c5cc 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3877,8 +3877,8 @@
// The activity that we are finishing may be over the lock screen. In this case, we do not
// want to consider activities that cannot be shown on the lock screen as running and should
// proceed with finishing the activity if there is no valid next top running activity.
- final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */);
+ final ActivityDisplay display = getDisplay();
+ final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible) {
@@ -3902,23 +3902,25 @@
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
r.setState(FINISHING, "finishCurrentActivityLocked");
- final boolean finishingActivityInNonFocusedStack
- = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
- && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
+ final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
+ && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+ || (next == null && display.topRunningActivity() == null));
if (mode == FINISH_IMMEDIATELY
|| (prevState == PAUSED
&& (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
- || finishingActivityInNonFocusedStack
+ || finishingInNonFocusedStackOrNoRunning
|| prevState == STOPPING
|| prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
- if (finishingActivityInNonFocusedStack) {
+ if (finishingInNonFocusedStackOrNoRunning) {
// Finishing activity that was in paused state and it was in not currently focused
- // stack, need to make something visible in its place.
+ // stack, need to make something visible in its place. Also if the display does not
+ // have running activity, the configuration may need to be updated for restoring
+ // original orientation of the display.
mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 12c6dec..257a004 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,8 +21,12 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_DEBUG;
+import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WaitResult.INVALID_DELAY;
@@ -54,6 +58,21 @@
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.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.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 @@
}
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 @@
if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
&& processName.equals(activity.processName)) {
try {
- if (realStartActivityLocked(activity, (ProcessRecord) app.mOwner,
+ if (realStartActivityLocked(activity, app,
top == activity /* andResume */, true /* checkConfig */)) {
didSomething = true;
}
@@ -1209,75 +1214,15 @@
}
ActivityRecord topRunningActivityLocked() {
- return topRunningActivityLocked(false /* considerKeyguardState */);
- }
-
- /**
- * Returns the top running activity in the focused stack. In the case the focused stack has no
- * such activity, the next focusable stack on top of a display is returned.
- * @param considerKeyguardState Indicates whether the locked state should be considered. if
- * {@code true} and the keyguard is locked, only activities that
- * can be shown on top of the keyguard will be considered.
- * @return The top running activity. {@code null} if none is available.
- */
- ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- ActivityRecord r = focusedStack.topRunningActivityLocked();
- if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
- return r;
- }
-
- // Look in other non-focused and non-home stacks.
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.get(i);
-
- // TODO: We probably want to consider the top fullscreen stack as we could have a pinned
- // stack on top.
- final ActivityStack topStack = display.getTopStack();
-
- // Only consider focusable top stacks other than the current focused one.
- if (topStack == null || !topStack.isFocusable() || topStack == focusedStack) {
- continue;
- }
-
- final ActivityRecord topActivity = topStack.topRunningActivityLocked();
-
- // Skip if no top activity.
- if (topActivity == null) {
- continue;
- }
-
-
- // This activity can be considered the top running activity if we are not
- // considering the locked state, the keyguard isn't locked, or we can show when
- // locked.
- if (isValidTopRunningActivity(topActivity, considerKeyguardState)) {
+ final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+ if (topActivity != null) {
return topActivity;
}
}
-
return null;
}
- /**
- * Verifies an {@link ActivityRecord} can be the top activity based on keyguard state and
- * whether we are considering it.
- */
- private boolean isValidTopRunningActivity(ActivityRecord record,
- boolean considerKeyguardState) {
- if (!considerKeyguardState) {
- return true;
- }
-
- final boolean keyguardLocked = getKeyguardController().isKeyguardLocked();
-
- if (!keyguardLocked) {
- return true;
- }
-
- return record.canShowWhenLocked();
- }
-
@VisibleForTesting
void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
@ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
@@ -1299,20 +1244,17 @@
// Don't debug things in the system process
if (!aInfo.processName.equals("system")) {
- if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
- mService.mAm.setDebugApp(aInfo.processName, true, false);
- }
-
- if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
- mService.mAm.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
- }
-
- if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
- mService.mAm.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
- }
-
- if (profilerInfo != null) {
- mService.mAm.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+ if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
+ | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
+ /**
+ * Assume safe to call into AMS synchronously because the call that set these
+ * flags should have originated from AMS which will already have its lock held.
+ * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String,
+ * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
+ * TODO(b/80414790): Investigate a better way of untangling this.
+ */
+ mService.mAmInternal.setDebugFlagsForStartingActivity(
+ aInfo, startFlags, profilerInfo);
}
}
final String intentLaunchToken = intent.getLaunchToken();
@@ -1359,7 +1301,7 @@
return resolveActivity(intent, rInfo, startFlags, profilerInfo);
}
- final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
+ private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
if (!allPausedActivitiesComplete()) {
@@ -1378,7 +1320,6 @@
beginDeferResume();
try {
- final WindowProcessController proc = app.getWindowProcessController();
r.startFreezingScreenLocked(proc, 0);
// schedule launch ticks to collect information about slow apps.
@@ -1414,15 +1355,15 @@
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
- if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
+ if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
Slog.wtf(TAG,
"User ID for activity changing for " + r
+ " appInfo.uid=" + r.appInfo.uid
+ " info.ai.uid=" + applicationInfoUid
- + " old=" + r.app + " new=" + app);
+ + " old=" + r.app + " new=" + proc);
}
- app.waitingToKill = null;
+ proc.clearWaitingToKill();
r.launchCount++;
r.lastLaunchTime = SystemClock.uptimeMillis();
@@ -1441,7 +1382,7 @@
}
try {
- if (app.thread == null) {
+ if (!proc.hasThread()) {
throw new RemoteException();
}
List<ResultInfo> results = null;
@@ -1467,50 +1408,29 @@
r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
- ProfilerInfo profilerInfo = null;
- if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
- if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
- mService.mAm.mProfileProc = app;
- ProfilerInfo profilerInfoSvc = mService.mAm.mProfilerInfo;
- if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
- if (profilerInfoSvc.profileFd != null) {
- try {
- profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
- } catch (IOException e) {
- profilerInfoSvc.closeFd();
- }
- }
+ ProfilerInfo profilerInfo = proc.onStartActivity(mService.mTopProcessState);
- profilerInfo = new ProfilerInfo(profilerInfoSvc);
- }
- }
- }
-
- app.hasShownUi = true;
- app.setPendingUiClean(true);
- app.forceProcessStateUpTo(mService.mTopProcessState);
// Because we could be starting an Activity in the system process this may not go
// across a Binder interface which would create a new Configuration. Consequently
// we have to always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- app.getWindowProcessController().getConfiguration(),
- r.getMergedOverrideConfiguration());
+ proc.getConfiguration(), r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
// Create activity launch transaction.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
- r.appToken);
+ final ClientTransaction clientTransaction = ClientTransaction.obtain(
+ proc.getThread(), r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
- r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+ r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.icicle, r.persistentState, results, newIntents,
mService.isNextTransitionForward(), profilerInfo));
@@ -1526,12 +1446,12 @@
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
- if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
+ if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mHasHeavyWeightFeature) {
// This may be a heavy-weight process! Note that the package manager will ensure
// that only activity can run in the main process of the .apk, which is the only
// thing that will be considered heavy-weight.
- if (app.processName.equals(app.info.packageName)) {
+ if (proc.mName.equals(proc.mInfo.packageName)) {
if (mService.mHeavyWeightProcess != null
&& mService.mHeavyWeightProcess != proc) {
Slog.w(TAG, "Starting new heavy weight process " + proc
@@ -1544,12 +1464,10 @@
} catch (RemoteException e) {
if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
+ // This is the second time we failed -- finish activity and give up.
Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- mService.mAm.appDiedLocked(app);
+ + r.intent.getComponent().flattenToShortString() + ", giving up", e);
+ proc.appDied();
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"2nd-crash", false);
return false;
@@ -1658,24 +1576,21 @@
}
}
- void startSpecificActivityLocked(ActivityRecord r,
- boolean andResume, boolean checkConfig) {
+ void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
- ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
- r.info.applicationInfo.uid, true);
+ final WindowProcessController wpc =
+ mService.getProcessController(r.processName, r.info.applicationInfo.uid);
- if (app != null && app.thread != null) {
+ if (wpc != null && wpc.hasThread()) {
try {
- if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
+ if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
- // Don't add this if it is a platform component that is marked
- // to run in multiple processes, because this is actually
- // part of the framework so doesn't make sense to track as a
- // separate apk in the process.
- app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
- mService.mAm.mProcessStats);
+ // Don't add this if it is a platform component that is marked to run in
+ // multiple processes, because this is actually part of the framework so doesn't
+ // make sense to track as a separate apk in the process.
+ wpc.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode);
}
- realStartActivityLocked(r, app, andResume, checkConfig);
+ realStartActivityLocked(r, wpc, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
@@ -1686,8 +1601,12 @@
// restart the application.
}
- mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent(), false, false, true);
+ // Post message to start process to avoid possible deadlock of calling into AMS with the
+ // ATMS lock held.
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
+ r.info.applicationInfo, true, "activity", r.intent.getComponent());
+ mService.mH.sendMessage(msg);
}
void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index afcf9f9..45a0652 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.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.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 @@
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 @@
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 @@
// 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 @@
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
- preferredLaunchDisplayId, mTargetStack);
+ mPreferredDisplayId, mTargetStack);
return START_SUCCESS;
}
@@ -1614,7 +1596,6 @@
} else {
mPreferredDisplayId = DEFAULT_DISPLAY;
}
- ensureValidPreferredDisplayId(r);
mLaunchMode = r.launchMode;
@@ -1707,24 +1688,6 @@
mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
}
- /**
- * Ensure preferred display ID matches the starting activity.
- */
- private void ensureValidPreferredDisplayId(ActivityRecord startingActivity) {
- // Check if the Activity is a VR activity. If so, the activity should be launched in
- // main display.
- if (startingActivity != null && startingActivity.requestedVrComponent != null) {
- mPreferredDisplayId = DEFAULT_DISPLAY;
- }
-
- // Get the virtual display ID from ActivityStackManagerService. If that's set we should
- // always use that.
- final int displayId = mService.mVr2dDisplayId;
- if (displayId != INVALID_DISPLAY) {
- mPreferredDisplayId = displayId;
- }
- }
-
private void sendNewTaskResultRequestIfNeeded() {
final ActivityStack sourceStack = mStartActivity.resultTo != null
? mStartActivity.resultTo.getStack() : null;
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 2d27017..f79d9aa 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 @@
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 @@
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 @@
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 @@
return newInfo;
}
- private WindowProcessController getProcessController(String processName, int uid) {
+ WindowProcessController getProcessController(String processName, int uid) {
if (uid == SYSTEM_UID) {
// The system gets to run in any process. If there are multiple processes with the same
// uid, just pick the first (this should never happen).
@@ -5896,6 +5864,9 @@
@Override
public void finishHeavyWeightApp() {
synchronized (mGlobalLock) {
+ if (mHeavyWeightProcess != null) {
+ mHeavyWeightProcess.finishActivities();
+ }
ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
mHeavyWeightProcess);
}
@@ -6247,20 +6218,12 @@
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 83c4ab5..e8ec057 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
} else {
// Huh.
Process.killProcess(pid);
- ActivityManagerService.killProcessGroup(uid, pid);
+ ProcessList.killProcessGroup(uid, pid);
}
}
return true;
@@ -714,7 +714,7 @@
// 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 a13cf4d..a0977be 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 @@
r.curApp = app;
app.curReceivers.add(r);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
- mService.updateLruProcessLocked(app, false, null);
+ mService.mProcessList.updateLruProcessLocked(app, false, null);
if (!skipOomAdj) {
mService.updateOomAdjLocked();
}
@@ -892,7 +892,7 @@
isDead = proc == null || proc.isCrashing();
}
} else {
- final ProcessRecord proc = mService.mProcessNames.get(
+ final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.pendingStart;
}
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 218d908..68e897f 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
@@ -97,6 +98,15 @@
break;
}
}
+
+ if (activity != null && activity.requestedVrComponent != null) {
+ // Check if the Activity is a VR activity. If so, it should be launched in main display.
+ result.mPreferredDisplayId = DEFAULT_DISPLAY;
+ } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
+ // Get the virtual display ID from ActivityTaskManagerService. If that's set we
+ // should always use that.
+ result.mPreferredDisplayId = mService.mVr2dDisplayId;
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 9e7ce32..3f172cc 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.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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
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 @@
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 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 @@
}
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 @@
}
}
}
+
+ 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 0eb535b..fa7a08b 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.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.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 @@
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 @@
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 @@
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 @@
* 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 @@
if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
- for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord r = mService.mLruProcesses.get(i);
+ for (int i = mService.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord r = mService.mProcessList.mLruProcesses.get(i);
if (r != null && r.thread != null) {
int myPid = r.pid;
if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index 792b66b..1743dde 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 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 @@
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 9cad6fe4..4a7e6e8 100644
--- a/services/core/java/com/android/server/am/WindowProcessListener.java
+++ b/services/core/java/com/android/server/am/WindowProcessListener.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.app.ProfilerInfo;
import android.content.pm.ApplicationInfo;
import android.util.proto.ProtoOutputStream;
@@ -51,5 +52,16 @@
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
long getCpuTime();
+ /** Clears the waiting to kill reason for this process. */
+ void clearWaitingToKill();
+
+ /** Adds the package to the process. */
+ void addPackage(String pkg, long versionCode);
+
+ /** Called when we are in the process on starting an activity. */
+ ProfilerInfo onStartActivity(int topProcessState);
+
+ /** App died :(...oh well */
+ void appDied();
void writeToProto(ProtoOutputStream proto, long fieldId);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fc76b46..d57214e 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.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.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.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 @@
*
* @see #mCurFocusedWindow
*/
+ @SoftInputModeFlags
int mCurFocusedWindowSoftInputMode;
/**
@@ -517,6 +522,7 @@
*
* @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
*/
+ @MissingMethodFlags
int mCurInputContextMissingMethods;
/**
@@ -690,20 +696,21 @@
final IBinder mImeToken;
@NonNull
final String mImeId;
- // @InputMethodClient.StartInputReason
+ @StartInputReason
final int mStartInputReason;
final boolean mRestarting;
@Nullable
final IBinder mTargetWindow;
@NonNull
final EditorInfo mEditorInfo;
+ @SoftInputModeFlags
final int mTargetWindowSoftInputMode;
final int mClientBindSequenceNumber;
StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId,
- /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting,
+ @StartInputReason int startInputReason, boolean restarting,
@Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo,
- int targetWindowSoftInputMode, int clientBindSequenceNumber) {
+ @SoftInputModeFlags int targetWindowSoftInputMode, int clientBindSequenceNumber) {
mSequenceNumber = sSequenceNumber.getAndIncrement();
mTimestamp = SystemClock.uptimeMillis();
mWallTime = System.currentTimeMillis();
@@ -771,13 +778,14 @@
String mImeTokenString;
@NonNull
String mImeId;
- /* @InputMethodClient.StartInputReason */
+ @StartInputReason
int mStartInputReason;
boolean mRestarting;
@NonNull
String mTargetWindowString;
@NonNull
EditorInfo mEditorInfo;
+ @SoftInputModeFlags
int mTargetWindowSoftInputMode;
int mClientBindSequenceNumber;
@@ -834,7 +842,7 @@
pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
+ " (timestamp=" + entry.mTimestamp + ")"
+ " reason="
- + InputMethodClient.getStartInputReason(entry.mStartInputReason)
+ + InputMethodDebug.startInputReasonToString(entry.mStartInputReason)
+ " restarting=" + entry.mRestarting);
pw.print(prefix);
@@ -846,7 +854,7 @@
+ " 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 @@
// 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 @@
* {@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 @@
}
}
- 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 @@
@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 @@
@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 @@
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 @@
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 @@
clearCurMethodLocked();
}
- void resetCurrentMethodAndClient(
- /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+ void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) {
mCurMethodId = null;
unbindCurrentMethodLocked();
unbindCurrentClientLocked(unbindClientReason);
@@ -2166,7 +2170,7 @@
mLastBindTime = SystemClock.uptimeMillis();
mShowRequested = mInputShown;
mInputShown = false;
- unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_DISCONNECT_IME);
+ unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
}
}
@@ -2477,12 +2481,12 @@
setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
- resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
+ resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
}
mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
- resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
+ resetCurrentMethodAndClient(UnbindReason.NO_IME);
}
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
@@ -2560,7 +2564,7 @@
intent.putExtra("input_method_id", id);
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
- unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
+ unbindCurrentClientLocked(UnbindReason.SWITCH_IME);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2734,11 +2738,10 @@
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
- int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- int unverifiedTargetSdkVersion) {
+ @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+ int controlFlags, @SoftInputModeFlags int softInputMode, int windowFlags,
+ @Nullable EditorInfo attribute, IInputContext inputContext,
+ @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -2749,7 +2752,7 @@
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
- + InputMethodClient.getStartInputReason(startInputReason)
+ + InputMethodDebug.startInputReasonToString(startInputReason)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " editorInfo=" + attribute);
return InputBindResult.NULL;
@@ -2759,12 +2762,10 @@
@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 @@
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 @@
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 3f8941d..4ece538d 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -57,6 +57,8 @@
* This isn't strictly necessary because each controller is only interested in a specific field,
* and the receivers that are listening for global state change will all run on the main looper,
* but we don't enforce that so this is safer.
+ *
+ * Test: atest com.android.server.job.controllers.JobStatusTest
* @hide
*/
public final class JobStatus {
@@ -154,7 +156,9 @@
// Constraints.
final int requiredConstraints;
+ private final int mRequiredConstraintsOfInterest;
int satisfiedConstraints = 0;
+ private int mSatisfiedConstraintsOfInterest = 0;
// Set to true if doze constraint was satisfied due to app being whitelisted.
public boolean dozeWhitelisted;
@@ -265,6 +269,28 @@
private long totalNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
+ /////// states change.
+
+ /**
+ * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
+ * should only run if its constraints are satisfied.
+ * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
+ */
+ private boolean mReadyDeadlineSatisfied;
+
+ /**
+ * The device isn't Dozing or this job will be in the foreground. This implicit constraint must
+ * be satisfied.
+ */
+ private boolean mReadyNotDozing;
+
+ /**
+ * The job is not restricted from running in the background (due to Battery Saver). This
+ * implicit constraint must be satisfied.
+ */
+ private boolean mReadyNotRestrictedInBg;
+
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
@@ -349,6 +375,8 @@
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
}
this.requiredConstraints = requiredConstraints;
+ mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
+ mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
@@ -865,7 +893,12 @@
}
boolean setDeadlineConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
+ if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
+ return true;
+ }
+ return false;
}
boolean setIdleConstraintSatisfied(boolean state) {
@@ -882,11 +915,21 @@
boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
dozeWhitelisted = whitelisted;
- return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
+ if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+ return true;
+ }
+ return false;
}
boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
+ if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyNotRestrictedInBg = state;
+ return true;
+ }
+ return false;
}
boolean setUidActive(final boolean newActiveState) {
@@ -903,6 +946,7 @@
return false;
}
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
+ mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
return true;
}
@@ -933,24 +977,15 @@
/**
* @return Whether or not this job is ready to run, based on its requirements. This is true if
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
- * TODO: This function is called a *lot*. We should probably just have it check an
- * already-computed boolean, which we updated whenever we see one of the states it depends
- * on here change.
*/
public boolean isReady() {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
// is an implementation detail. A periodic job should only run if its constraints are
// satisfied).
- // AppNotIdle implicit constraint must be satisfied
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
- final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
- && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
- final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
- || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
- final boolean notRestrictedInBg =
- (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
- return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
+ return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
+ || isConstraintsSatisfied());
}
static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -971,15 +1006,13 @@
return true;
}
- final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
-
- int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+ int sat = mSatisfiedConstraintsOfInterest;
if (overrideState == OVERRIDE_SOFT) {
// override: pretend all 'soft' requirements are satisfied
sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
}
- return (sat & req) == req;
+ return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
}
public boolean matches(int uid, int jobId) {
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index b29b7cf..99a04d7 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -20,6 +20,7 @@
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.IContexthub;
import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
@@ -57,9 +58,9 @@
private final ContextHubClientManager mClientManager;
/*
- * The ID of the hub that this client is attached to.
+ * The object describing the hub that this client is attached to.
*/
- private final int mAttachedContextHubId;
+ private final ContextHubInfo mAttachedContextHubInfo;
/*
* The host end point ID of this client.
@@ -85,11 +86,12 @@
/* package */ ContextHubClientBroker(
Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
- int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+ ContextHubInfo contextHubInfo, short hostEndPointId,
+ IContextHubClientCallback callback) {
mContext = context;
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
- mAttachedContextHubId = contextHubId;
+ mAttachedContextHubInfo = contextHubInfo;
mHostEndPointId = hostEndPointId;
mCallbackInterface = callback;
}
@@ -119,11 +121,12 @@
ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
mHostEndPointId, message);
+ int contextHubId = mAttachedContextHubInfo.getId();
try {
- result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+ result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
- + mAttachedContextHubId + ")", e);
+ + contextHubId + ")", e);
result = Result.UNKNOWN_FAILURE;
}
} else {
@@ -156,7 +159,7 @@
* @return the ID of the context hub this client is attached to
*/
/* package */ int getAttachedContextHubId() {
- return mAttachedContextHubId;
+ return mAttachedContextHubInfo.getId();
}
/**
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 4243f02..eda8c6f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -19,13 +19,13 @@
import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.ContextHubInfo;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
import android.hardware.location.NanoAppMessage;
import android.os.RemoteException;
import android.util.Log;
-import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -80,15 +80,15 @@
* Registers a new client with the service.
*
* @param clientCallback the callback interface of the client to register
- * @param contextHubId the ID of the hub this client is attached to
+ * @param contextHubInfo the object describing the hub this client is attached to
*
* @return the client interface
*
* @throws IllegalStateException if max number of clients have already registered
*/
/* package */ IContextHubClient registerClient(
- IContextHubClientCallback clientCallback, int contextHubId) {
- ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+ IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+ ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
try {
broker.attachDeathRecipient();
@@ -183,14 +183,14 @@
* manager.
*
* @param clientCallback the callback interface of the client to register
- * @param contextHubId the ID of the hub this client is attached to
+ * @param contextHubInfo the object describing the hub this client is attached to
*
* @return the ContextHubClientBroker object
*
* @throws IllegalStateException if max number of clients have already registered
*/
private synchronized ContextHubClientBroker createNewClientBroker(
- IContextHubClientCallback clientCallback, int contextHubId) {
+ IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
throw new IllegalStateException("Could not register client - max limit exceeded");
}
@@ -198,10 +198,11 @@
ContextHubClientBroker broker = null;
int id = mNextHostEndpointId;
for (int i = 0; i <= MAX_CLIENT_ID; i++) {
- if (!mHostEndPointIdToClientMap.containsKey((short)id)) {
+ if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
broker = new ContextHubClientBroker(
- mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
- mHostEndPointIdToClientMap.put((short)id, broker);
+ mContext, mContextHubProxy, this, contextHubInfo, (short) id,
+ clientCallback);
+ mHostEndPointIdToClientMap.put((short) id, broker);
mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
break;
}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 27509de..96e9337 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -170,8 +170,9 @@
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+ ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
IContextHubClient client = mClientManager.registerClient(
- createDefaultClientCallback(contextHubId), contextHubId);
+ createDefaultClientCallback(contextHubId), contextHubInfo);
defaultClientMap.put(contextHubId, client);
try {
@@ -623,7 +624,8 @@
throw new NullPointerException("Cannot register client with null callback");
}
- return mClientManager.registerClient(clientCallback, contextHubId);
+ ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
+ return mClientManager.registerClient(clientCallback, contextHubInfo);
}
/**
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 452b699..4f4b6bf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -72,6 +72,7 @@
static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
static final int NTWK_BLOCKED_BG_RESTRICT = 5;
static final int NTWK_ALLOWED_DEFAULT = 6;
+ static final int NTWK_ALLOWED_SYSTEM = 7;
private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 61d67b7..099671d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,6 +16,8 @@
package com.android.server.net;
+import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
+
import android.net.Network;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionPlan;
@@ -46,6 +48,28 @@
public abstract boolean isUidNetworkingBlocked(int uid, String ifname);
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ */
+ public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted) {
+ // Log of invoking internal function is disabled because it will be called very
+ // frequently. And metrics are unlikely needed on this method because the callers are
+ // external and this method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ /**
* Informs that an appId has been added or removed from the temp-powersave-whitelist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 48e09d7..d799642 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -99,6 +99,7 @@
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
@@ -4837,46 +4838,75 @@
final long startTime = mStatLogger.getTime();
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
-
- mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
-
- return ret;
- }
-
- private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) {
final int uidRules;
final boolean isBackgroundRestricted;
synchronized (mUidRulesFirstLock) {
uidRules = mUidRules.get(uid, RULE_NONE);
isBackgroundRestricted = mRestrictBackground;
}
- if (hasRule(uidRules, RULE_REJECT_ALL)) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
- return true;
+ final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, mLogger);
+
+ mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+ return ret;
+ }
+
+ private static boolean isSystem(int uid) {
+ return uid < Process.FIRST_APPLICATION_UID;
+ }
+
+ static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
+ final int reason;
+ // Networks are never blocked for system components
+ if (isSystem(uid)) {
+ reason = NTWK_ALLOWED_SYSTEM;
}
- if (!isNetworkMetered) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
- return false;
+ else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+ reason = NTWK_BLOCKED_POWER;
}
- if (hasRule(uidRules, RULE_REJECT_METERED)) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
- return true;
+ else if (!isNetworkMetered) {
+ reason = NTWK_ALLOWED_NON_METERED;
}
- if (hasRule(uidRules, RULE_ALLOW_METERED)) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
- return false;
+ else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+ reason = NTWK_BLOCKED_BLACKLIST;
}
- if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
- return false;
+ else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+ reason = NTWK_ALLOWED_WHITELIST;
}
- if (isBackgroundRestricted) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
- return true;
+ else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+ reason = NTWK_ALLOWED_TMP_WHITELIST;
}
- mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
- return false;
+ else if (isBackgroundRestricted) {
+ reason = NTWK_BLOCKED_BG_RESTRICT;
+ }
+ else {
+ reason = NTWK_ALLOWED_DEFAULT;
+ }
+
+ final boolean blocked;
+ switch(reason) {
+ case NTWK_ALLOWED_DEFAULT:
+ case NTWK_ALLOWED_NON_METERED:
+ case NTWK_ALLOWED_TMP_WHITELIST:
+ case NTWK_ALLOWED_WHITELIST:
+ case NTWK_ALLOWED_SYSTEM:
+ blocked = false;
+ break;
+ case NTWK_BLOCKED_POWER:
+ case NTWK_BLOCKED_BLACKLIST:
+ case NTWK_BLOCKED_BG_RESTRICT:
+ blocked = true;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ if (logger != null) {
+ logger.networkBlocked(uid, reason);
+ }
+
+ return blocked;
}
private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
@@ -4918,11 +4948,18 @@
public boolean isUidNetworkingBlocked(int uid, String ifname) {
final long startTime = mStatLogger.getTime();
+ final int uidRules;
+ final boolean isBackgroundRestricted;
+ synchronized (mUidRulesFirstLock) {
+ uidRules = mUidRules.get(uid, RULE_NONE);
+ isBackgroundRestricted = mRestrictBackground;
+ }
final boolean isNetworkMetered;
synchronized (mNetworkPoliciesSecondLock) {
isNetworkMetered = mMeteredIfaces.contains(ifname);
}
- final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+ final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, mLogger);
mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 506cc44..348b8e6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -719,8 +719,7 @@
return;
}
final long now = System.currentTimeMillis();
- MetricsLogger.action(r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ MetricsLogger.action(r.getItemLogMaker()
.setType(MetricsEvent.TYPE_ACTION)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
@@ -865,8 +864,7 @@
r.stats.onExpansionChanged(userAction, expanded);
final long now = System.currentTimeMillis();
if (userAction) {
- MetricsLogger.action(r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ MetricsLogger.action(r.getItemLogMaker()
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
}
@@ -5842,8 +5840,7 @@
mArchive.record(r.sbn);
final long now = System.currentTimeMillis();
- final LogMaker logMaker = r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ final LogMaker logMaker = r.getItemLogMaker()
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(reason);
if (rank != -1 && count != -1) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 65ec580..19a62d9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -611,6 +611,7 @@
}
public void applyAdjustments() {
+ long now = System.currentTimeMillis();
synchronized (mAdjustments) {
for (Adjustment adjustment: mAdjustments) {
Bundle signals = adjustment.getSignals();
@@ -618,17 +619,25 @@
final ArrayList<String> people =
adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
setPeopleOverride(people);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_PEOPLE, people.size()));
}
if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
final ArrayList<SnoozeCriterion> snoozeCriterionList =
adjustment.getSignals().getParcelableArrayList(
Adjustment.KEY_SNOOZE_CRITERIA);
setSnoozeCriteria(snoozeCriterionList);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SNOOZE_CRITERIA,
+ snoozeCriterionList.size()));
}
if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
final String groupOverrideKey =
adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
setOverrideGroupKey(groupOverrideKey);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_GROUP_KEY,
+ groupOverrideKey));
}
if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
// Only allow user sentiment update from assistant if user hasn't already
@@ -637,19 +646,31 @@
&& (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
setUserSentiment(adjustment.getSignals().getInt(
Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_USER_SENTIMENT,
+ getUserSentiment()));
}
}
if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
+ getSmartActions().size()));
}
if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
+ getSmartReplies().size()));
}
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
importance = Math.min(IMPORTANCE_HIGH, importance);
setAssistantImportance(importance);
+ MetricsLogger.action(getAdjustmentLogMaker()
+ .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_IMPORTANCE,
+ importance));
}
}
}
@@ -1203,6 +1224,16 @@
return getLogMaker(System.currentTimeMillis());
}
+ public LogMaker getItemLogMaker() {
+ return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
+ }
+
+ public LogMaker getAdjustmentLogMaker() {
+ return getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+ .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT);
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 760f155..44b80c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1212,27 +1212,89 @@
}
private final class Metrics extends Callback {
- private static final String COUNTER_PREFIX = "dnd_mode_";
+ private static final String COUNTER_MODE_PREFIX = "dnd_mode_";
+ private static final String COUNTER_TYPE_PREFIX = "dnd_type_";
+ private static final int DND_OFF = 0;
+ private static final int DND_ON_MANUAL = 1;
+ private static final int DND_ON_AUTOMATIC = 2;
+ private static final String COUNTER_RULE = "dnd_rule_count";
private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
+ // Total silence, alarms only, priority only
private int mPreviousZenMode = -1;
- private long mBeginningMs = 0L;
+ private long mModeLogTimeMs = 0L;
+
+ private int mNumZenRules = -1;
+ private long mRuleCountLogTime = 0L;
+
+ // automatic (1) vs manual (0) vs dnd off (2)
+ private int mPreviousZenType = -1;
+ private long mTypeLogTimeMs = 0L;
@Override
void onZenModeChanged() {
emit();
}
+ @Override
+ void onConfigChanged() {
+ emit();
+ }
+
private void emit() {
mHandler.postMetricsTimer();
+ emitZenMode();
+ emitRules();
+ emitDndType();
+ }
+
+ private void emitZenMode() {
final long now = SystemClock.elapsedRealtime();
- final long since = (now - mBeginningMs);
+ final long since = (now - mModeLogTimeMs);
if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) {
if (mPreviousZenMode != -1) {
- MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since);
+ MetricsLogger.count(
+ mContext, COUNTER_MODE_PREFIX + mPreviousZenMode, (int) since);
}
mPreviousZenMode = mZenMode;
- mBeginningMs = now;
+ mModeLogTimeMs = now;
+ }
+ }
+
+ private void emitRules() {
+ final long now = SystemClock.elapsedRealtime();
+ final long since = (now - mRuleCountLogTime);
+ synchronized (mConfig) {
+ int numZenRules = mConfig.automaticRules.size();
+ if (mNumZenRules != numZenRules
+ || since > MINIMUM_LOG_PERIOD_MS) {
+ if (mNumZenRules != -1) {
+ MetricsLogger.count(mContext, COUNTER_RULE,
+ numZenRules - mNumZenRules);
+ }
+ mNumZenRules = numZenRules;
+
+ mRuleCountLogTime = since;
+ }
+ }
+ }
+
+ private void emitDndType() {
+ final long now = SystemClock.elapsedRealtime();
+ final long since = (now - mTypeLogTimeMs);
+ synchronized (mConfig) {
+ boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
+ int zenType = !dndOn ? DND_OFF
+ : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
+ if (zenType != mPreviousZenType
+ || since > MINIMUM_LOG_PERIOD_MS) {
+ if (mPreviousZenType != -1) {
+ MetricsLogger.count(
+ mContext, COUNTER_TYPE_PREFIX + mPreviousZenType, (int) since);
+ }
+ mTypeLogTimeMs = now;
+ mPreviousZenType = zenType;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index be24c71..5569822 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -243,7 +243,7 @@
}
/**
- * Called by {@link PowerManagerService} to update the battery saver stete.
+ * Called by {@link PowerManagerService} to update the battery saver state.
*/
public void enableBatterySaver(boolean enable, int reason) {
synchronized (mLock) {
@@ -290,8 +290,8 @@
* This method is called only in the following cases:
* - When battery saver becomes activated.
* - When battery saver becomes deactivated.
- * - When battery saver is on the interactive state changes.
- * - When battery saver is on the battery saver policy changes.
+ * - When battery saver is on and the interactive state changes.
+ * - When battery saver is on and the battery saver policy changes.
*/
void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
final LowPowerModeListener[] listeners;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 15f6938..ed3e6c6 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -435,14 +435,14 @@
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
if (recentsAnimationController != null
- && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+ && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
if (recentsAnimationController.updateInputConsumerForApp(
recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
mAddRecentsAnimationInputConsumerHandle = false;
}
- // Skip adding the window below regardless of whether there is an input consumer
- // to handle it
+ // If the target app window does not yet exist, then we don't add the input
+ // consumer window, but also don't add the app window below.
return;
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6fef1630..5c80759 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -458,10 +458,9 @@
mRunner = null;
mCanceled = true;
- // Clear associated input consumers
+ // Update the input windows after the animation is complete
final InputMonitor inputMonitor =
mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
- inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
inputMonitor.updateInputWindowsLw(true /*force*/);
// We have deferred all notifications to the target app as a part of the recents animation,
@@ -494,6 +493,11 @@
@Override
public void binderDied() {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
+
+ // Clear associated input consumers on runner death
+ final InputMonitor inputMonitor =
+ mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+ inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -516,8 +520,14 @@
&& isTargetOverWallpaper();
}
- boolean hasInputConsumerForApp(AppWindowToken appToken) {
- return mInputConsumerEnabled && isAnimatingApp(appToken);
+ /**
+ * @return Whether to use the input consumer to override app input to route home/recents.
+ */
+ boolean shouldApplyInputConsumer(AppWindowToken appToken) {
+ // Only apply the input consumer if it is enabled, it is not the target (home/recents)
+ // being revealed with the transition, and we are actively animating the app as a part of
+ // the animation
+ return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken);
}
boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
@@ -675,6 +685,7 @@
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+ pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 21e807e..6fd17955 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.graphics.Bitmap.CompressFormat.*;
+
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -361,6 +362,7 @@
// For snapshots with reduced resolution, do not create or save full sized bitmaps
if (mSnapshot.isReducedResolution()) {
+ swBitmap.recycle();
return true;
}
@@ -373,6 +375,8 @@
Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
return false;
}
+ reduced.recycle();
+ swBitmap.recycle();
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7342ba1..056e92e 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 void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
synchronized (mWindowMap) {
if (mRecentsAnimationController != null) {
- mRecentsAnimationController.cleanupAnimation(reorderMode);
+ final RecentsAnimationController controller = mRecentsAnimationController;
mRecentsAnimationController = null;
+ controller.cleanupAnimation(reorderMode);
mAppTransition.updateBooster();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index b06be1a..2dbbf55 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 @@
}
@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 70cdba2..b88165e 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.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 @@
}
@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 @@
}
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
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
rename to services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1752479..b63138e 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -16,19 +16,27 @@
package com.android.server.job.controllers;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.junit.Assert.assertEquals;
import android.app.job.JobInfo;
import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
import android.os.SystemClock;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
import java.time.Clock;
import java.time.ZoneOffset;
@@ -37,8 +45,17 @@
public class JobStatusTest {
private static final double DELTA = 0.00001;
+ private MockitoSession mMockingSession;
+
@Before
public void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+ doReturn(mock(PackageManagerInternal.class))
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
@@ -48,6 +65,13 @@
Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
}
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
@Test
public void testFraction() throws Exception {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 1eb88ba..113ee2d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -26,13 +26,21 @@
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
+import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.Process.SYSTEM_UID;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -124,6 +132,7 @@
import android.text.format.Time;
import android.util.DataUnit;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -171,6 +180,7 @@
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
@@ -1644,6 +1654,76 @@
true);
}
+ /**
+ * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * conditions.
+ */
+ @Test
+ public void testIsUidNetworkingBlocked() {
+ final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
+
+ // Metered network. Data saver on.
+ expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+
+ // Metered network. Data saver off.
+ expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+
+ // Non-metered network. Data saver on.
+ expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+
+ // Non-metered network. Data saver off. The result is the same as previous case since
+ // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
+ verifyNetworkBlockedState(
+ false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+ }
+
+ private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
+ ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
+ final NetworkPolicyManagerInternal npmi = LocalServices
+ .getService(NetworkPolicyManagerInternal.class);
+
+ for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
+ final boolean expectedResult = pair.first;
+ final int rule = pair.second;
+ assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
+ expectedResult,
+ npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
+ npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ }
+ }
+
+ private String formatBlockedStateError(int uid, int rule, boolean metered,
+ boolean backgroundRestricted) {
+ return String.format(
+ "Unexpected BlockedState: (uid=%d, rule=%s, metered=%b, backgroundRestricted=%b)",
+ uid, uidRulesToString(rule), metered, backgroundRestricted);
+ }
+
private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
return SubscriptionPlan.Builder
.createRecurringMonthly(start)
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
index ea90ffd..0da5742 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -20,11 +20,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
import android.platform.test.annotations.Presubmit;
@@ -131,4 +134,57 @@
new ActivityBuilder(mService).setTask(fullscreenTask).build();
return fullscreenStack;
}
+
+ /**
+ * Verifies the correct activity is returned when querying the top running activity.
+ */
+ @Test
+ public void testTopRunningActivity() {
+ // Create stack to hold focus.
+ final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+ final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ final KeyguardController keyguard = mSupervisor.getKeyguardController();
+ final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(stack).build();
+
+ // Make sure the top running activity is not affected when keyguard is not locked.
+ assertTopRunningActivity(activity, display);
+
+ // Check to make sure activity not reported when it cannot show on lock and lock is on.
+ doReturn(true).when(keyguard).isKeyguardLocked();
+ assertEquals(activity, display.topRunningActivity());
+ assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+ // Change focus to stack with activity.
+ stack.moveToFront("focusChangeToTestStack");
+ assertEquals(stack, display.getFocusedStack());
+ assertEquals(activity, display.topRunningActivity());
+ assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+ // Add activity that should be shown on the keyguard.
+ final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(stack)
+ .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+ .build();
+
+ // Ensure the show when locked activity is returned.
+ assertTopRunningActivity(showWhenLockedActivity, display);
+
+ // Change focus back to empty stack.
+ emptyStack.moveToFront("focusChangeToEmptyStack");
+ assertEquals(emptyStack, display.getFocusedStack());
+ // If there is no running activity in focused stack, the running activity in next focusable
+ // stack should be returned.
+ assertTopRunningActivity(showWhenLockedActivity, display);
+ }
+
+ private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
+ assertEquals(top, display.topRunningActivity());
+ assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index c44e492..f42f5b1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -64,6 +64,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -102,6 +103,7 @@
* com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
*/
@SmallTest
+@FlakyTest(bugId = 113616538)
@RunWith(AndroidJUnit4.class)
public class ActivityManagerServiceTest {
private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
@@ -173,7 +175,7 @@
true); // expectNotify
// Explicitly setting the seq counter for more verification.
- mAms.mProcStateSeqCounter = 42;
+ mAms.mProcessList.mProcStateSeqCounter = 42;
// Uid state is not moving from background to foreground or vice versa.
verifySeqCounterAndInteractions(uidRec,
@@ -260,7 +262,7 @@
final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid, null);
appRec.thread = Mockito.mock(IApplicationThread.class);
- mAms.mLruProcesses.add(appRec);
+ mAms.mProcessList.mLruProcesses.add(appRec);
return uidRec;
}
@@ -275,11 +277,11 @@
uidRec.curProcState = curState;
mAms.incrementProcStateSeqAndNotifyAppsLocked();
- assertEquals(expectedGlobalCounter, mAms.mProcStateSeqCounter);
+ assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
- for (int i = mAms.mLruProcesses.size() - 1; i >= 0; --i) {
- final ProcessRecord app = mAms.mLruProcesses.get(i);
+ for (int i = mAms.mProcessList.getLruSizeLocked() - 1; i >= 0; --i) {
+ final ProcessRecord app = mAms.mProcessList.mLruProcesses.get(i);
// AMS should notify apps only for block states other than NETWORK_STATE_NO_CHANGE.
if (app.uid == uidRec.uid && expectedBlockState == NETWORK_STATE_BLOCK) {
verify(app.thread).setNetworkBlockSeq(uidRec.curProcStateSeq);
@@ -829,4 +831,4 @@
mRestricted = restricted;
}
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index ba25b16..2dfb375 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -16,38 +16,59 @@
package com.android.server.am;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
import android.app.IActivityManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.RemoteException;
-import android.test.AndroidTestCase;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
import java.util.List;
-public class ActivityManagerTest extends AndroidTestCase {
+import androidx.test.filters.FlakyTest;
- IActivityManager service;
- @Override
+/**
+ * Tests for {@link ActivityManager}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.am.ActivityManagerTest
+ */
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class ActivityManagerTest {
+
+ private IActivityManager service;
+
+ @Before
public void setUp() throws Exception {
- super.setUp();
service = ActivityManager.getService();
}
+ @Test
public void testTaskIdsForRunningUsers() throws RemoteException {
- for(int userId : service.getRunningUserIds()) {
+ int[] runningUserIds = service.getRunningUserIds();
+ assertThat(runningUserIds).isNotEmpty();
+ for (int userId : runningUserIds) {
testTaskIdsForUser(userId);
}
}
private void testTaskIdsForUser(int userId) throws RemoteException {
- List<ActivityManager.RecentTaskInfo> recentTasks = service.getRecentTasks(
- 100, 0, userId).getList();
- if(recentTasks != null) {
- for(ActivityManager.RecentTaskInfo recentTask : recentTasks) {
- int taskId = recentTask.persistentId;
+ List<?> recentTasks = service.getRecentTasks(100, 0, userId).getList();
+ if (recentTasks != null) {
+ for (Object elem : recentTasks) {
+ assertThat(elem).isInstanceOf(RecentTaskInfo.class);
+ RecentTaskInfo recentTask = (RecentTaskInfo) elem;
+ int taskId = recentTask.taskId;
assertEquals("The task id " + taskId + " should not belong to user " + userId,
- taskId / UserHandle.PER_USER_RANGE, userId);
+ taskId / UserHandle.PER_USER_RANGE, userId);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index cc7a24d..2c993d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -25,7 +25,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
@@ -307,62 +306,6 @@
}
/**
- * Verifies the correct activity is returned when querying the top running activity.
- */
- @Test
- public void testTopRunningActivity() throws Exception {
- // Create stack to hold focus
- final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
- final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
- .setStack(stack).build();
-
- // Make sure the top running activity is not affected when keyguard is not locked
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Check to make sure activity not reported when it cannot show on lock and lock is on.
- doReturn(true).when(keyguard).isKeyguardLocked();
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Change focus to stack with activity.
- stack.moveToFront("focusChangeToTestStack");
- assertEquals(stack, display.getFocusedStack());
- assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Add activity that should be shown on the keyguard.
- final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
- .setCreateTask(true)
- .setStack(stack)
- .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
- .build();
-
- // Ensure the show when locked activity is returned.
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
-
- // Change focus back to empty stack
- emptyStack.moveToFront("focusChangeToEmptyStack");
- assertEquals(emptyStack, display.getFocusedStack());
- // Looking for running activity only in top and focused stack, so nothing should be returned
- // from empty stack.
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
- true /* considerKeyguardState */));
- }
-
- /**
* Verify that split-screen primary stack will be chosen if activity is launched that targets
* split-screen secondary, but a matching existing instance is found on top of split-screen
* primary stack.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 5fcd2aa..53f67af 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -26,6 +26,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
@@ -35,10 +38,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
@@ -655,6 +662,39 @@
}
@Test
+ public void testFinishCurrentActivity() {
+ // Create 2 activities on a new display.
+ final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // There is still an activity1 in stack1 so the activity2 should be added to finishing list
+ // that will be destroyed until idle.
+ final ActivityRecord activity2 = finishCurrentActivity(stack2);
+ assertEquals(FINISHING, activity2.getState());
+ assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
+
+ // The display becomes empty. Since there is no next activity to be idle, the activity
+ // should be destroyed immediately with updating configuration to restore original state.
+ final ActivityRecord activity1 = finishCurrentActivity(stack1);
+ assertEquals(DESTROYING, activity1.getState());
+ verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+ eq(display.mDisplayId), anyBoolean(), anyBoolean());
+ }
+
+ private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+ final ActivityRecord activity = stack.topRunningActivityLocked();
+ assertNotNull(activity);
+ activity.setState(PAUSED, "finishCurrentActivity");
+ activity.makeFinishingLocked();
+ stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE,
+ false /* oomAdj */, "finishCurrentActivity");
+ return activity;
+ }
+
+ @Test
public void testShouldSleepActivities() throws Exception {
// When focused activity and keyguard is going away, we should not sleep regardless
// of the display state
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 58fe70d..01d51e4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -58,8 +58,9 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.HandlerThread;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
@@ -69,7 +70,9 @@
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
+import com.android.server.ServiceThread;
import com.android.server.wm.AppWindowContainerController;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.RootWindowContainerController;
@@ -82,6 +85,7 @@
import org.junit.Before;
import org.mockito.MockitoAnnotations;
+import java.io.File;
import java.util.List;
/**
@@ -97,7 +101,7 @@
new DexmakerShareClassLoaderRule();
private final Context mContext = InstrumentationRegistry.getContext();
- private HandlerThread mHandlerThread;
+ final TestInjector mTestInjector = new TestInjector();
ActivityTaskManagerService mService;
ActivityStackSupervisor mSupervisor;
@@ -115,13 +119,12 @@
MockitoAnnotations.initMocks(this);
AttributeCache.init(mContext);
}
- mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
- mHandlerThread.start();
+ mTestInjector.setUp();
}
@After
public void tearDown() {
- mHandlerThread.quitSafely();
+ mTestInjector.tearDown();
}
protected ActivityTaskManagerService createActivityTaskManagerService() {
@@ -143,7 +146,7 @@
}
ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
- final TestActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+ final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
setupActivityManagerService(am, atm);
return am;
}
@@ -173,6 +176,7 @@
doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+ am.mActivityTaskManager = atm;
am.mWindowManager = prepareMockWindowManager();
atm.setWindowManager(am.mWindowManager);
@@ -192,8 +196,6 @@
// An id appended to the end of the component name to make it unique
private static int sCurrentActivityId = 0;
-
-
private final ActivityTaskManagerService mService;
private ComponentName mComponent;
@@ -487,6 +489,40 @@
}
}
+ private static class TestInjector extends ActivityManagerService.Injector {
+ private ServiceThread mHandlerThread;
+
+ @Override
+ public Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return null;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public boolean isNetworkRestrictedForUid(int uid) {
+ return false;
+ }
+
+ void setUp() {
+ mHandlerThread = new ServiceThread("ActivityTestsThread",
+ Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+ mHandlerThread.start();
+ }
+
+ void tearDown() {
+ mHandlerThread.quitSafely();
+ }
+ }
+
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
@@ -495,8 +531,8 @@
private ActivityManagerInternal mInternal;
- TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
- super(context, atm);
+ TestActivityManagerService(TestInjector testInjector) {
+ super(testInjector, testInjector.mHandlerThread);
mUgmInternal = mock(UriGrantsManagerInternal.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 3819e21..06d41f1 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -21,6 +21,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,6 +38,7 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
+@FlakyTest(bugId = 113616538)
public class AppErrorDialogTest {
private Context mContext;
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index a030210..1b823ff 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
import android.view.IWindowManager;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -67,6 +68,7 @@
* runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
*/
@MediumTest
+@FlakyTest(bugId = 113616538)
@RunWith(AndroidJUnit4.class)
public class AssistDataRequesterTest extends ActivityTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 62c5734..75f7c4c 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -45,7 +45,7 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class BroadcastRecordTest extends ActivityTestsBase {
+public class BroadcastRecordTest {
@Test
public void testCleanupDisabledPackageReceivers() {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 1276f65..27e8c63 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -116,8 +116,7 @@
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
mService = spy(new MyTestActivityTaskManagerService(mContext));
- final TestActivityManagerService am =
- spy(new MyTestActivityManagerService(mContext, mService));
+ final TestActivityManagerService am = spy(new MyTestActivityManagerService());
setupActivityManagerService(am, mService);
mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
@@ -848,8 +847,8 @@
}
private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
- super(context, atm);
+ MyTestActivityManagerService() {
+ super(mTestInjector);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 8d54bc2..48bfe1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -16,34 +16,43 @@
package com.android.server.am;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
import android.content.pm.UserInfo;
-import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
-import android.test.AndroidTestCase;
-import android.util.Log;
+import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
-import com.android.server.am.TaskPersister;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
-import java.io.File;
-import java.util.Random;
+import androidx.test.filters.FlakyTest;
/**
- * atest FrameworksServicesTests:TaskPersisterTest
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:TaskPersisterTest
*/
-public class TaskPersisterTest extends AndroidTestCase {
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class TaskPersisterTest {
private static final String TEST_USER_NAME = "AM-Test-User";
private TaskPersister mTaskPersister;
private int testUserId;
private UserManager mUserManager;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- mUserManager = UserManager.get(getContext());
- mTaskPersister = new TaskPersister(getContext().getFilesDir());
+ mUserManager = UserManager.get(getTargetContext());
+ mTaskPersister = new TaskPersister(getTargetContext().getFilesDir());
// In ARC, the maximum number of supported users is one, which is different from the ones of
// most phones (more than 4). This prevents TaskPersisterTest from creating another user for
// test. However, since guest users can be added as much as possible, we create guest user
@@ -51,9 +60,8 @@
testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
}
- @Override
+ @After
public void tearDown() throws Exception {
- super.tearDown();
mTaskPersister.unloadUserDataFromMemory(testUserId);
removeUser(testUserId);
}
@@ -64,6 +72,7 @@
return taskId;
}
+ @Test
public void testTaskIdsPersistence() {
SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
for (int i = 0; i < 100; i++) {
@@ -72,21 +81,18 @@
mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
SparseBooleanArray newTaskIdsOnFile = mTaskPersister
.loadPersistedTaskIdsForUser(testUserId);
- assertTrue("TaskIds written differ from TaskIds read back from file",
- taskIdsOnFile.equals(newTaskIdsOnFile));
+ assertEquals("TaskIds written differ from TaskIds read back from file",
+ taskIdsOnFile, newTaskIdsOnFile);
}
private int createUser(String name, int flags) {
UserInfo user = mUserManager.createUser(name, flags);
- if (user == null) {
- fail("Error while creating the test user: " + TEST_USER_NAME);
- }
+ assertNotNull("Error while creating the test user: " + TEST_USER_NAME, user);
return user.id;
}
private void removeUser(int userId) {
- if (!mUserManager.removeUser(userId)) {
- fail("Error while removing the test user: " + TEST_USER_NAME);
- }
+ boolean userRemoved = mUserManager.removeUser(userId);
+ assertTrue("Error while removing the test user: " + TEST_USER_NAME, userRemoved);
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 921cdea..27766d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -69,13 +69,11 @@
private static final String TASK_TAG = "task";
- private ActivityTaskManagerService mService;
-
@Before
public void setUp() throws Exception {
super.setUp();
TaskRecord.setTaskRecordFactory(null);
- mService = createActivityTaskManagerService();
+ setupActivityTaskManagerService();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index cc4f519..75e1d0d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,41 @@
package com.android.server.am;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+
+import static com.google.android.collect.Lists.newArrayList;
+import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -31,80 +66,62 @@
import android.os.RemoteException;
import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
-import org.mockito.Mockito;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
-import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import androidx.test.filters.SmallTest;
/**
- * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ * Tests for {@link UserController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.am.UserControllerTest
*/
@Presubmit
-public class UserControllerTest extends AndroidTestCase {
+@SmallTest
+public class UserControllerTest {
private static final int TEST_USER_ID = 10;
private static final int NONEXIST_USER_ID = 2;
private static String TAG = UserControllerTest.class.getSimpleName();
private UserController mUserController;
private TestInjector mInjector;
- private static final List<String> START_FOREGROUND_USER_ACTIONS =
- Arrays.asList(
- Intent.ACTION_USER_STARTED,
- Intent.ACTION_USER_SWITCHED,
- Intent.ACTION_USER_STARTING);
+ private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
+ Intent.ACTION_USER_STARTED,
+ Intent.ACTION_USER_SWITCHED,
+ Intent.ACTION_USER_STARTING);
- private static final List<String> START_BACKGROUND_USER_ACTIONS =
- Arrays.asList(
- Intent.ACTION_USER_STARTED,
- Intent.ACTION_LOCKED_BOOT_COMPLETED,
- Intent.ACTION_USER_STARTING);
+ private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
+ Intent.ACTION_USER_STARTED,
+ Intent.ACTION_LOCKED_BOOT_COMPLETED,
+ Intent.ACTION_USER_STARTING);
- private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES =
- new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
- SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
+ private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
+ REPORT_USER_SWITCH_MSG,
+ USER_SWITCH_TIMEOUT_MSG,
+ SYSTEM_USER_START_MSG,
+ SYSTEM_USER_CURRENT_MSG);
- private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES =
- new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG));
+ private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
+ SYSTEM_USER_START_MSG,
+ REPORT_LOCKED_BOOT_COMPLETE_MSG);
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
runWithDexmakerShareClassLoader(() -> {
- mInjector = Mockito.spy(new TestInjector(getContext()));
+ mInjector = spy(new TestInjector(getTargetContext()));
doNothing().when(mInjector).clearAllLockedTasks(anyString());
doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
@@ -114,58 +131,54 @@
});
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
mInjector.handlerThread.quit();
- Mockito.validateMockitoUsage();
+ validateMockitoUsage();
}
- @SmallTest
- public void testStartUser_foreground() throws RemoteException {
+ @Test
+ public void testStartUser_foreground() {
mUserController.startUser(TEST_USER_ID, true /* foreground */);
- Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
- Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
- Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
- Mockito.verify(mInjector).clearAllLockedTasks(anyString());
+ verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+ verify(mInjector.getWindowManager()).setSwitchingUser(true);
+ verify(mInjector).clearAllLockedTasks(anyString());
startForegroundUserAssertions();
}
- @SmallTest
- public void testStartUser_background() throws RemoteException {
+ @Test
+ public void testStartUser_background() {
mUserController.startUser(TEST_USER_ID, false /* foreground */);
- Mockito.verify(
- mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
- Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector, never()).clearAllLockedTasks(anyString());
+ verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ verify(mInjector, never()).clearAllLockedTasks(anyString());
startBackgroundUserAssertions();
}
- @SmallTest
- public void testStartUserUIDisabled() throws RemoteException {
+ @Test
+ public void testStartUserUIDisabled() {
mUserController.mUserSwitchUiEnabled = false;
mUserController.startUser(TEST_USER_ID, true /* foreground */);
- Mockito.verify(mInjector.getWindowManager(), never())
- .startFreezingScreen(anyInt(), anyInt());
- Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
- Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
startForegroundUserAssertions();
}
private void startUserAssertions(
- List<String> expectedActions, Set<Integer> expectedMessageCodes)
- throws RemoteException {
+ List<String> expectedActions, Set<Integer> expectedMessageCodes) {
assertEquals(expectedActions, getActions(mInjector.sentIntents));
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
}
- private void startBackgroundUserAssertions() throws RemoteException {
+ private void startBackgroundUserAssertions() {
startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
}
- private void startForegroundUserAssertions() throws RemoteException {
+ private void startForegroundUserAssertions() {
startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
@@ -177,15 +190,15 @@
assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2);
}
- @SmallTest
- public void testFailedStartUserInForeground() throws RemoteException {
+ @Test
+ public void testFailedStartUserInForeground() {
mUserController.mUserSwitchUiEnabled = false;
mUserController.startUserInForeground(NONEXIST_USER_ID);
- Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(false);
+ verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+ verify(mInjector.getWindowManager()).setSwitchingUser(false);
}
- @SmallTest
+ @Test
public void testDispatchUserSwitch() throws RemoteException {
// Prepare mock observer and register it
IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -206,7 +219,7 @@
// Call dispatchUserSwitch and verify that observer was called only once
mInjector.handler.clearAllRecordedMessages();
mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+ verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG);
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -220,7 +233,7 @@
assertEquals("Unexpected new user id", TEST_USER_ID, conMsg.arg2);
}
- @SmallTest
+ @Test
public void testDispatchUserSwitchBadReceiver() throws RemoteException {
// Prepare mock observer which doesn't notify the callback and register it
IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -236,14 +249,14 @@
// Call dispatchUserSwitch and verify that observer was called only once
mInjector.handler.clearAllRecordedMessages();
mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+ verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
// Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout)
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
- assertTrue("No messages should be sent", actualCodes.isEmpty());
+ assertWithMessage("No messages should be sent").that(actualCodes).isEmpty();
}
- @SmallTest
- public void testContinueUserSwitch() throws RemoteException {
+ @Test
+ public void testContinueUserSwitch() {
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, true);
Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -254,12 +267,12 @@
mInjector.handler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
mUserController.continueUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
continueUserSwitchAssertions();
}
- @SmallTest
- public void testContinueUserSwitchUIDisabled() throws RemoteException {
+ @Test
+ public void testContinueUserSwitchUIDisabled() {
mUserController.mUserSwitchUiEnabled = false;
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, true);
@@ -271,11 +284,11 @@
mInjector.handler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
mUserController.continueUserSwitch(userState, oldUserId, newUserId);
- Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
continueUserSwitchAssertions();
}
- private void continueUserSwitchAssertions() throws RemoteException {
+ private void continueUserSwitchAssertions() {
Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG);
Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -284,7 +297,7 @@
assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1);
}
- @SmallTest
+ @Test
public void testDispatchUserSwitchComplete() throws RemoteException {
// Prepare mock observer and register it
IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -298,12 +311,12 @@
mInjector.handler.clearAllRecordedMessages();
// Mockito can't reset only interactions, so just verify that this hasn't been
// called with 'false' until after dispatchUserSwitchComplete.
- Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
// Call dispatchUserSwitchComplete
mUserController.dispatchUserSwitchComplete(newUserId);
- Mockito.verify(observer, times(1)).onUserSwitchComplete(anyInt());
- Mockito.verify(observer).onUserSwitchComplete(TEST_USER_ID);
- Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+ verify(observer, times(1)).onUserSwitchComplete(anyInt());
+ verify(observer).onUserSwitchComplete(TEST_USER_ID);
+ verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
}
private void setUpUser(int userId, int flags) {
@@ -320,7 +333,7 @@
}
// Should be public to allow mocking
- public static class TestInjector extends UserController.Injector {
+ private static class TestInjector extends UserController.Injector {
TestHandler handler;
TestHandler uiHandler;
HandlerThread handlerThread;
@@ -438,4 +451,4 @@
return super.sendMessageAtTime(msg, uptimeMillis);
}
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index ced0847..f8e7403 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -30,6 +30,7 @@
import android.platform.test.annotations.Presubmit;
import android.view.InputChannel;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -43,6 +44,7 @@
* atest com.android.server.wm.TaskPositioningControllerTests
*/
@SmallTest
+@FlakyTest(bugId = 117924387)
@RunWith(AndroidJUnit4.class)
@Presubmit
public class TaskPositioningControllerTests extends WindowTestsBase {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3127b35..d33a537 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -676,7 +676,7 @@
/**
* @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 1efa906..d9e7167 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.job.JobService;
@@ -2889,100 +2890,131 @@
public static final String SUBSCRIPTION_ID = "sub_id";
/**
- * The profile_id to which the APN saved in modem
+ * The profile_id to which the APN saved in modem.
* <p>Type: INTEGER</p>
*@hide
*/
public static final String PROFILE_ID = "profile_id";
/**
- * Is the apn setting to be set in modem
- * <P>Type: INTEGER (boolean)</P>
+ * If set to {@code true}, then the APN setting will persist to the modem.
+ * <p>Type: INTEGER (boolean)</p>
*@hide
*/
+ @SystemApi
public static final String MODEM_COGNITIVE = "modem_cognitive";
/**
- * The max connections of this apn
+ * The max connections of this APN.
* <p>Type: INTEGER</p>
*@hide
*/
+ @SystemApi
public static final String MAX_CONNS = "max_conns";
/**
- * The wait time for retry of the apn
+ * The wait time for retry of the APN.
* <p>Type: INTEGER</p>
*@hide
*/
+ @SystemApi
public static final String WAIT_TIME = "wait_time";
/**
- * The time to limit max connection for the apn
+ * The time to limit max connection for the APN.
* <p>Type: INTEGER</p>
*@hide
*/
+ @SystemApi
public static final String MAX_CONNS_TIME = "max_conns_time";
/**
- * The MTU size of the mobile interface to which the APN connected
+ * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected.
* <p>Type: INTEGER </p>
* @hide
*/
+ @SystemApi
public static final String MTU = "mtu";
/**
- * Is this APN added/edited/deleted by a user or carrier?
+ * APN edit status. APN could be added/edited/deleted by a user or carrier.
* <p>Type: INTEGER </p>
* @hide
*/
+ @SystemApi
public static final String EDITED = "edited";
/**
- * Is this APN visible to the user?
- * <p>Type: INTEGER (boolean) </p>
+ * {@code true} if this APN visible to the user, {@code false} otherwise.
+ * <p>Type: INTEGER (boolean)</p>
* @hide
*/
+ @SystemApi
public static final String USER_VISIBLE = "user_visible";
/**
- * Is the user allowed to edit this APN?
- * <p>Type: INTEGER (boolean) </p>
+ * {@code true} if the user allowed to edit this APN, {@code false} otherwise.
+ * <p>Type: INTEGER (boolean)</p>
* @hide
*/
+ @SystemApi
public static final String USER_EDITABLE = "user_editable";
/**
- * Following are possible values for the EDITED field
+ * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to
+ * edit.
+ * <p>Type: INTEGER </p>
* @hide
*/
+ @SystemApi
public static final int UNEDITED = 0;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} indicates that this APN has been edited by users.
+ * <p>Type: INTEGER </p>
+ * @hide
*/
+ @SystemApi
public static final int USER_EDITED = 1;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} indicates that this APN has been deleted by users.
+ * <p>Type: INTEGER </p>
+ * @hide
*/
+ @SystemApi
public static final int USER_DELETED = 2;
+
/**
- * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
- * by the user is still present in the new APN database and therefore must remain tagged
- * as user deleted rather than completely removed from the database
+ * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+ * deleted by the user is still present in the new APN database and therefore must remain
+ * tagged as user deleted rather than completely removed from the database.
* @hide
*/
public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers.
+ * <p>Type: INTEGER </p>
+ * @hide
*/
+ @SystemApi
public static final int CARRIER_EDITED = 4;
+
/**
- * CARRIER_DELETED values are currently not used as there is no usecase. If they are used,
+ * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers.
+ * CARRIER_DELETED values are currently not used as there is no use case. If they are used,
* delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+ * <p>Type: INTEGER </p>
* @hide
*/
public static final int CARRIER_DELETED = 5;
+
/**
- * @hide
+ * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+ * deleted by the carrier is still present in the new APN database and therefore must remain
+ * tagged as user deleted rather than completely removed from the database.
+ * @hide
*/
public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
@@ -3011,16 +3043,20 @@
* The APN set id. When the user manually selects an APN or the framework sets an APN as
* preferred, all APNs with the same set id as the selected APN should be prioritized over
* APNs in other sets.
+ * <p>Type: INTEGER</p>
* @hide
*/
+ @SystemApi
public static final String APN_SET_ID = "apn_set_id";
/**
- * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
- * user manually selects an APN with no set set, there is no need to prioritize any specific
- * APN set ids.
+ * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a
+ * set. If the user manually selects an APN with no set set, there is no need to prioritize
+ * any specific APN set ids.
+ * <p>Type: INTEGER</p>
* @hide
*/
+ @SystemApi
public static final int NO_SET_SET = 0;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 08b5c2d..b0997f1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1202,6 +1202,20 @@
"always_show_data_rat_icon_bool";
/**
+ * Boolean indicating if default data account should show LTE or 4G icon
+ * @hide
+ */
+ public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL =
+ "show_4g_for_lte_data_icon_bool";
+
+ /**
+ * Boolean indicating if lte+ icon should be shown if available
+ * @hide
+ */
+ public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL =
+ "hide_lte_plus_data_icon_bool";
+
+ /**
* Boolean to decide whether to show precise call failed cause to user
* @hide
*/
@@ -2530,6 +2544,8 @@
sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true);
+ sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/telephony/java/android/telephony/RcsManager.java b/telephony/java/android/telephony/RcsManager.java
new file mode 100644
index 0000000..00ce03a
--- /dev/null
+++ b/telephony/java/android/telephony/RcsManager.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IRcs;
+
+/**
+ * RcsManager is the application interface to RcsProvider and provides access methods to
+ * RCS related database tables.
+ * @hide - TODO make this public
+ */
+public class RcsManager {
+ private static final String TAG = "RcsManager";
+ private static final boolean VDBG = false;
+
+ /**
+ * Delete the RcsThread identified by the given threadId.
+ * @param threadId threadId of the thread to be deleted.
+ */
+ public void deleteThread(int threadId) {
+ if (VDBG) logd("deleteThread: threadId: " + threadId);
+ try {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+ if (iRcs != null) {
+ iRcs.deleteThread(threadId);
+ }
+ } catch (RemoteException re) {
+
+ }
+ }
+
+ private static void logd(String msg) {
+ Rlog.d(TAG, msg);
+ }
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bfbcd57..e0ec2c5 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -21,7 +21,6 @@
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 @@
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 @@
@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 @@
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public int getVoiceRegState() {
return mVoiceRegState;
}
@@ -472,7 +471,7 @@
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public int getDataRegState() {
return mDataRegState;
}
@@ -533,7 +532,7 @@
* @return roaming status
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public boolean getVoiceRoaming() {
return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING;
}
@@ -557,7 +556,7 @@
* @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 d0c6c49..3b40164 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1368,7 +1368,7 @@
}
/** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public static int getPhoneId(int subId) {
if (!isValidSubscriptionId(subId)) {
if (DBG) {
@@ -1664,7 +1664,7 @@
* 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 @@
}
/** @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 d6a0ae1..69901d2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17,6 +17,7 @@
package android.telephony;
import static android.content.Context.TELECOM_SERVICE;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
@@ -59,6 +60,7 @@
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.Log;
@@ -230,8 +232,7 @@
/** @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 @@
}
/** {@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 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @UnsupportedAppUsage
public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
"android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
@@ -1231,6 +1232,38 @@
*/
public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
+ /**
+ * Broadcast intent action indicating that the telephony provider DB got lost.
+ *
+ * <p>
+ * The {@link #EXTRA_IS_CORRUPTED} extra indicates whether the database is lost
+ * due to corruption or not
+ *
+ * <p class="note">
+ * Requires the MODIFY_PHONE_STATE permission.
+ *
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ *
+ * @see #EXTRA_IS_CORRUPTED
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public static final String ACTION_MMSSMS_DATABASE_LOST =
+ "android.intent.action.MMSSMS_DATABASE_LOST";
+
+ /**
+ * A boolean extra used with {@link #ACTION_MMSSMS_DATABASE_LOST} to indicate
+ * whether the database is lost due to corruption or not.
+ *
+ * @see #ACTION_MMSSMS_DATABASE_LOST
+ *
+ * @hide
+ */
+ public static final String EXTRA_IS_CORRUPTED = "isCorrupted";
+
//
//
// Device Info
@@ -1278,11 +1311,11 @@
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
* MEID for CDMA.
@@ -1307,11 +1340,11 @@
* Returns the unique device ID of a subscription, for example, the IMEI for
* GSM and the MEID for CDMA phones. Return null if device ID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param slotIndex of which deviceID is returned
*
@@ -1339,11 +1372,11 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // No support for device / profile owner.
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1355,11 +1388,11 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param slotIndex of which IMEI is returned
*/
@@ -1408,11 +1441,11 @@
/**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // No support for device / profile owner.
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1423,11 +1456,11 @@
/**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
- * device or profile owner. The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+ * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+ * that owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param slotIndex of which MEID is returned
*/
@@ -1732,7 +1765,7 @@
}
/** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @UnsupportedAppUsage
private int getPhoneTypeFromProperty(int phoneId) {
String type = getTelephonyProperty(phoneId,
TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
@@ -1916,7 +1949,7 @@
* @param subId
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getNetworkOperatorName(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, "");
@@ -1944,7 +1977,7 @@
* @param subId
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getNetworkOperator(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getNetworkOperatorForPhone(phoneId);
@@ -2268,7 +2301,7 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public int getDataNetworkType(int subId) {
try{
ITelephony telephony = getITelephony();
@@ -2304,7 +2337,7 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public int getVoiceNetworkType(int subId) {
try{
ITelephony telephony = getITelephony();
@@ -2787,7 +2820,7 @@
* @param subId for which SimOperator is returned
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSimOperator(int subId) {
return getSimOperatorNumeric(subId);
}
@@ -2801,7 +2834,7 @@
* @see #getSimState
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSimOperatorNumeric() {
int subId = mSubId;
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
@@ -2830,7 +2863,7 @@
* @param subId for which SimOperator is returned
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSimOperatorNumeric(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimOperatorNumericForPhone(phoneId);
@@ -2844,7 +2877,7 @@
* @param phoneId for which SimOperator is returned
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSimOperatorNumericForPhone(int phoneId) {
return getTelephonyProperty(phoneId,
TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
@@ -2871,7 +2904,7 @@
* @param subId for which SimOperatorName is returned
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSimOperatorName(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimOperatorNameForPhone(phoneId);
@@ -2901,7 +2934,7 @@
* @param subId for which SimCountryIso is returned
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSimCountryIso(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getSimCountryIsoForPhone(phoneId);
@@ -2923,11 +2956,11 @@
* unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2940,11 +2973,11 @@
* unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param subId for which Sim Serial number is returned
* @hide
@@ -3085,11 +3118,11 @@
* Return null if it is unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3103,17 +3136,17 @@
* Return null if it is unavailable.
*
* <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner, or that the calling app has carrier privileges (see {@link
- * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
- * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
- * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
- * release.
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
*
* @param subId whose subscriber id is returned
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getSubscriberId(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -3498,7 +3531,7 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public String getMsisdn(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
@@ -4431,7 +4464,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
@@ -7404,7 +7437,9 @@
@UnsupportedAppUsage
public boolean isVolteAvailable() {
try {
- return getITelephony().isVolteAvailable(getSubId());
+ return getITelephony().isAvailable(getSubId(),
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
} catch (RemoteException | NullPointerException ex) {
return false;
}
@@ -8009,7 +8044,7 @@
* either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @UnsupportedAppUsage
public ServiceState getServiceStateForSubscriber(int subId) {
try {
ITelephony service = getITelephony();
@@ -8343,20 +8378,31 @@
}
/**
- * Action set from carrier signalling broadcast receivers to enable/disable metered apns
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
- * @param subId the subscription ID that this action applies to.
- * @param enabled control enable or disable metered apns.
+ * Used to enable or disable carrier data by the system based on carrier signalling or
+ * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
+ * user settings, carrier data on/off won't affect user settings but will bypass the
+ * settings and turns off data internally if set to {@code false}.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ *
+ * @param enabled control enable or disable carrier data.
* @hide
*/
- public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setCarrierDataEnabled(boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.carrierActionSetMeteredApnsEnabled(subId, enabled);
+ service.carrierActionSetMeteredApnsEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled);
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e);
+ Log.e(TAG, "Error calling ITelephony#setCarrierDataEnabled", e);
}
}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
new file mode 100644
index 0000000..c9cf473
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -0,0 +1,760 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
+ * subscription.
+ *
+ * Allows a user to query the IMS MmTel feature information for a subscription, register for
+ * registration and MmTel capability status callbacks, as well as query/modify user settings for the
+ * associated subscription.
+ *
+ * @see #createForSubscriptionId(Context, int)
+ * @hide
+ */
+public class ImsMmTelManager {
+
+ private static final String TAG = "ImsMmTelManager";
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "WIFI_MODE_", value = {
+ WIFI_MODE_WIFI_ONLY,
+ WIFI_MODE_CELLULAR_PREFERRED,
+ WIFI_MODE_WIFI_PREFERRED
+ })
+ public @interface WiFiCallingMode {}
+
+ /**
+ * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
+ * registration if signal quality degrades.
+ * @hide
+ */
+ @SystemApi
+ public static final int WIFI_MODE_WIFI_ONLY = 0;
+
+ /**
+ * Prefer registering for IMS over LTE if LTE signal quality is high enough.
+ * @hide
+ */
+ @SystemApi
+ public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
+
+ /**
+ * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
+ * @hide
+ */
+ @SystemApi
+ public static final int WIFI_MODE_WIFI_PREFERRED = 2;
+
+ /**
+ * Callback class for receiving Registration callback events.
+ * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+ * @see #removeImsRegistrationCallback(RegistrationCallback)
+ */
+ public static class RegistrationCallback {
+
+ private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+ private final RegistrationCallback mLocalCallback;
+ private Executor mExecutor;
+
+ RegistrationBinder(RegistrationCallback localCallback) {
+ mLocalCallback = localCallback;
+ }
+
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+ }
+
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onDeregistered(info)));
+ }
+
+ @Override
+ public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+ }
+
+ @Override
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+ /**
+ * Notifies the framework when the IMS Provider is registered to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ */
+ public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ */
+ public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public void onDeregistered(ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type,
+ * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ *
+ * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
+ * failed
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ public void onTechnologyChangeFailed(
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+ }
+
+ /**
+ * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+ * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+ * allocated to a user for their own usage. A user's phone number is typically one of the
+ * associated URIs.
+ * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+ * subscription.
+ * @hide
+ */
+ public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+ }
+
+ /**@hide*/
+ public final IImsRegistrationCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ //Only exposed as public for compatibility with deprecated ImsManager APIs.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ /**
+ * Receives IMS capability status updates from the ImsService.
+ *
+ * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+ * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ */
+ public static class CapabilityCallback {
+
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+ private final CapabilityCallback mLocalCallback;
+ private Executor mExecutor;
+
+ CapabilityBinder(CapabilityCallback c) {
+ mLocalCallback = c;
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+ if (mLocalCallback == null) return;
+
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
+ new MmTelFeature.MmTelCapabilities(config))));
+ }
+
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ // This is not used for public interfaces.
+ }
+
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsFeature.ImsCapabilityError int reason) {
+ // This is not used for public interfaces
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+ /**
+ * The status of the feature's capabilities has changed to either available or unavailable.
+ * If unavailable, the feature is not able to support the unavailable capability at this
+ * time.
+ *
+ * @param capabilities The new availability of the capabilities.
+ */
+ public void onCapabilitiesStatusChanged(
+ MmTelFeature.MmTelCapabilities capabilities) {
+ }
+
+ /**@hide*/
+ public final IImsCapabilityCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ // Only exposed as public method for compatibility with deprecated ImsManager APIs.
+ // TODO: clean up dependencies and change back to private visibility.
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
+ private Context mContext;
+ private int mSubId;
+
+ /**
+ * Create an instance of ImsManager for the subscription id specified.
+ *
+ * @param context
+ * @param subId The ID of the subscription that this ImsManager will use.
+ * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ * @throws IllegalArgumentException if the subscription is invalid or
+ * the subscription ID is not an active subscription.
+ */
+ public static ImsMmTelManager createForSubscriptionId(Context context, int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)
+ || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
+ return new ImsMmTelManager(context, subId);
+ }
+
+ private ImsMmTelManager(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Registers a {@link RegistrationCallback} with the system, which will provide registration
+ * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
+ * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+ * after a subscription is removed.
+ * @param executor The executor the callback events should be run on.
+ * @param c The {@link RegistrationCallback} to be added.
+ * @see #removeImsRegistrationCallback(RegistrationCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+ @NonNull RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ try {
+ getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning
+ * up to avoid memory leaks or when the subscription is removed.
+ * @param c The {@link RegistrationCallback} to be removed.
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ try {
+ getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability
+ * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
+ * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+ * subscription changed events and call
+ * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
+ * is removed.
+ * @param executor The executor the callback events should be run on.
+ * @param c The MmTel {@link CapabilityCallback} to be registered.
+ * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+ @NonNull CapabilityCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ c.setExecutor(executor);
+ try {
+ getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
+ * up to avoid memory leaks.
+ * @param c The MmTel {@link CapabilityCallback} to be removed.
+ * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+ if (c == null) {
+ throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+ }
+ try {
+ getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the user's setting for whether or not to use MmTel capabilities over IMS,
+ * such as voice and video, depending on carrier configuration for the current subscription.
+ * @see #setAdvancedCallingSetting(boolean)
+ * @return true if the user’s setting for advanced calling is enabled and false otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isAdvancedCallingSettingEnabled() {
+ try {
+ return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+ * enable MmTel IMS features, such as voice and video calling, depending on the carrier
+ * configuration for the current subscription. Modifying this value may also trigger an IMS
+ * registration or deregistration, depending on the new value.
+ * @see #isAdvancedCallingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setAdvancedCallingSetting(boolean isEnabled) {
+ try {
+ getITelephony().setAdvancedCallingSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the IMS MmTel capability for a given registration technology. This does not
+ * necessarily mean that we are registered and the capability is available, but rather the
+ * subscription is capable of this service over IMS.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+ * @see #isAvailable(int, int)
+ *
+ * @param imsRegTech The IMS registration technology, can be one of the following:
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * @param capability The IMS MmTel capability to query, can be one of the following:
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
+ * otherwise.
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ try {
+ return getITelephony().isCapable(mSubId, capability, imsRegTech,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Query the availability of an IMS MmTel capability for a given registration technology. If
+ * a capability is available, IMS is registered and the service is currently available over IMS.
+ *
+ * @see #isCapable(int, int)
+ *
+ * @param imsRegTech The IMS registration technology, can be one of the following:
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * @param capability The IMS MmTel capability to query, can be one of the following:
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ * @return {@code true} if the MmTel IMS capability is available for this subscription, false
+ * otherwise.
+ */
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ try {
+ return getITelephony().isAvailable(mSubId, capability, imsRegTech,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * The user's setting for whether or not they have enabled the "Video Calling" setting.
+ * @return true if the user’s “Video Calling” setting is currently enabled.
+ * @see #setVtSetting(boolean)
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public boolean isVtSettingEnabled() {
+ try {
+ return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+ * @see #isVtSettingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVtSetting(boolean isEnabled) {
+ try {
+ getITelephony().setVtSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+ * @see #setVoWiFiSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isVoWiFiSettingEnabled() {
+ try {
+ return getITelephony().isVoWiFiSettingEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Sets the user's setting for whether or not Voice over WiFi is enabled.
+ * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
+ * @see #isVoWiFiSettingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiSetting(boolean isEnabled) {
+ try {
+ getITelephony().setVoWiFiSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
+ * if disabled.
+ * @see #setVoWiFiRoamingSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isVoWiFiRoamingSettingEnabled() {
+ try {
+ return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for Voice over WiFi while roaming.
+ * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
+ * false otherwise.
+ * @see #isVoWiFiRoamingSettingEnabled()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiRoamingSetting(boolean isEnabled) {
+ try {
+ getITelephony().setVoWiFiRoamingSetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
+ * Typically used during the Voice over WiFi registration process for some carriers.
+ *
+ * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
+ * otherwise.
+ * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #setVoWiFiSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+ try {
+ getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return The Voice over WiFi Mode preference set by the user, which can be one of the
+ * following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #setVoWiFiSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @WiFiCallingMode int getVoWiFiModeSetting() {
+ try {
+ return getITelephony().getVoWiFiModeSetting(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi calling mode.
+ * @param mode The user's preference for the technology to register for IMS over, can be one of
+ * the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #getVoWiFiModeSetting()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+ try {
+ getITelephony().setVoWiFiModeSetting(mSubId, mode);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
+ * another network.
+ *
+ * @return The user's preference for the technology to register for IMS over when roaming on
+ * another network, can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #setVoWiFiRoamingSetting(boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+ try {
+ return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the user's preference for Voice over WiFi mode while the device is roaming on another
+ * network.
+ *
+ * @param mode The user's preference for the technology to register for IMS over when roaming on
+ * another network, can be one of the following:
+ * - {@link #WIFI_MODE_WIFI_ONLY}
+ * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+ * - {@link #WIFI_MODE_WIFI_PREFERRED}
+ * @see #getVoWiFiRoamingModeSetting()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+ try {
+ getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Change the user's setting for RTT capability of this device.
+ * @param isEnabled if true RTT will be enabled during calls.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRttCapabilitySetting(boolean isEnabled) {
+ try {
+ getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+ return;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * @return true if TTY over VoLTE is supported
+ * @see android.telecom.TelecomManager#getCurrentTtyMode
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ boolean isTtyOverVolteEnabled() {
+ try {
+ return getITelephony().isTtyOverVolteEnabled(mSubId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ private static SubscriptionManager getSubscriptionManager(Context context) {
+ SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+ if (manager == null) {
+ throw new RuntimeException("Could not find SubscriptionManager.");
+ }
+ return manager;
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ if (binder == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+ return binder;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 4f37caa..749b191 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -23,7 +23,7 @@
import android.telephony.ims.ImsReasonInfo;
/**
- * See ImsRegistrationImplBase.Callback for more information.
+ * See {@link ImsManager#RegistrationCallback} for more information.
*
* {@hide}
*/
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b77881e..7f69f43 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -167,59 +167,6 @@
*/
public static final int CAPABILITY_SUCCESS = 0;
-
- /**
- * The framework implements this callback in order to register for Feature Capability status
- * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
- * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
- * callbacks when the ImsService can not change the capability as requested, via
- * {@link #onChangeCapabilityConfigurationError}.
- *
- * @hide
- */
- public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
- @Override
- public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
- onCapabilitiesStatusChanged(new Capabilities(config));
- }
-
- /**
- * Returns the result of a query for the capability configuration of a requested capability.
- *
- * @param capability The capability that was requested.
- * @param radioTech The IMS radio technology associated with the capability.
- * @param isEnabled true if the capability is enabled, false otherwise.
- */
- @Override
- public void onQueryCapabilityConfiguration(int capability, int radioTech,
- boolean isEnabled) {
-
- }
-
- /**
- * Called when a change to the capability configuration has returned an error.
- *
- * @param capability The capability that was requested to be changed.
- * @param radioTech The IMS radio technology associated with the capability.
- * @param reason error associated with the failure to change configuration.
- */
- @Override
- public void onChangeCapabilityConfigurationError(int capability, int radioTech,
- @ImsCapabilityError int reason) {
- }
-
- /**
- * The status of the feature's capabilities has changed to either available or unavailable.
- * If unavailable, the feature is not able to support the unavailable capability at this
- * time.
- *
- * @param config The new availability of the capabilities.
- */
- public void onCapabilitiesStatusChanged(Capabilities config) {
- }
- }
-
/**
* Used by the ImsFeature to call back to the CapabilityCallback that the framework has
* provided.
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7681aef..96995943 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -17,6 +17,8 @@
package android.telephony.ims.feature;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Message;
@@ -222,21 +224,31 @@
* This MmTelFeature can then return the status of each of these capabilities (enabled or not)
* by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
* status can also be queried using {@link #queryCapabilityStatus()}.
+ * @see #isCapable(int)
*/
public static class MmTelCapabilities extends Capabilities {
/**
- * @hide
+ * Create a new empty {@link MmTelCapabilities} instance.
+ * @see #addCapabilities(int)
+ * @see #removeCapabilities(int)
*/
@VisibleForTesting
public MmTelCapabilities() {
super();
}
+ /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/
+ @Deprecated
public MmTelCapabilities(Capabilities c) {
mCapabilities = c.mCapabilities;
}
+ /**
+ * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities that are supported for MmTel in the form of a
+ * bitfield.
+ */
public MmTelCapabilities(int capabilities) {
mCapabilities = capabilities;
}
@@ -406,7 +418,10 @@
* support the capability that is enabled. A capability that is disabled by the framework (via
* {@link #changeEnabledCapabilities}) should also show the status as disabled.
*/
- public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+ public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
+ if (c == null) {
+ throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
+ }
super.notifyCapabilitiesStatusChanged(c);
}
@@ -414,7 +429,12 @@
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
*/
- public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+ public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
+ @NonNull Bundle extras) {
+ if (c == null || extras == null) {
+ throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+ + "null.");
+ }
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
@@ -434,7 +454,12 @@
* This can be null if no call information is available for the rejected call.
* @param reason The {@link ImsReasonInfo} call rejection reason.
*/
- public final void notifyRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
+ public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
+ @NonNull ImsReasonInfo reason) {
+ if (callProfile == null || reason == null) {
+ throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+ + "null.");
+ }
synchronized (mLock) {
if (mListener == null) {
throw new IllegalStateException("Session is not available.");
@@ -508,8 +533,8 @@
* the framework.
*/
@Override
- public void changeEnabledCapabilities(CapabilityChangeRequest request,
- CapabilityCallbackProxy c) {
+ public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+ @NonNull CapabilityCallbackProxy c) {
// Base implementation, no-op
}
@@ -531,7 +556,7 @@
* {@link ImsCallProfile#CALL_TYPE_VS_RX}
* @return a {@link ImsCallProfile} object
*/
- public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+ public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
// Base Implementation - Should be overridden
return null;
}
@@ -552,7 +577,7 @@
*
* @param profile a call profile to make the call
*/
- public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+ public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
// Base Implementation - Should be overridden
return null;
}
@@ -569,7 +594,7 @@
* @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
* call will be placed over IMS or via CSFB.
*/
- public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+ public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
return PROCESS_CALL_IMS;
}
@@ -602,7 +627,7 @@
* @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
* configuration.
*/
- public ImsUtImplBase getUt() {
+ public @NonNull ImsUtImplBase getUt() {
// Base Implementation - Should be overridden
return new ImsUtImplBase();
}
@@ -611,7 +636,7 @@
* @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
* calls that support it.
*/
- public ImsEcbmImplBase getEcbm() {
+ public @NonNull ImsEcbmImplBase getEcbm() {
// Base Implementation - Should be overridden
return new ImsEcbmImplBase();
}
@@ -620,7 +645,7 @@
* @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
* package processing for multi-endpoint.
*/
- public ImsMultiEndpointImplBase getMultiEndpoint() {
+ public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
// Base Implementation - Should be overridden
return new ImsMultiEndpointImplBase();
}
@@ -646,7 +671,7 @@
* }
* }
*/
- public void setUiTtyMode(int mode, Message onCompleteMessage) {
+ public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
// Base Implementation - Should be overridden
}
@@ -680,7 +705,7 @@
* @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
* Provider.
*/
- public ImsSmsImplBase getSmsImplementation() {
+ public @NonNull ImsSmsImplBase getSmsImplementation() {
return new ImsSmsImplBase();
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index cecf2e2..a08e031 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -76,64 +76,6 @@
private static final int REGISTRATION_STATE_REGISTERING = 1;
private static final int REGISTRATION_STATE_REGISTERED = 2;
- /**
- * Callback class for receiving Registration callback events.
- * @hide
- */
- public static class Callback extends IImsRegistrationCallback.Stub {
- /**
- * Notifies the framework when the IMS Provider is connected to the IMS network.
- *
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationTech}.
- */
- @Override
- public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
- }
-
- /**
- * Notifies the framework when the IMS Provider is trying to connect the IMS network.
- *
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationTech}.
- */
- @Override
- public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
- }
-
- /**
- * Notifies the framework when the IMS Provider is disconnected from the IMS network.
- *
- * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
- */
- @Override
- public void onDeregistered(ImsReasonInfo info) {
- }
-
- /**
- * A failure has occurred when trying to handover registration to another technology type,
- * defined in {@link ImsRegistrationTech}
- *
- * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
- * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
- */
- @Override
- public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
- ImsReasonInfo info) {
- }
-
- /**
- * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
- * it changes.
- * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
- * subscription.
- */
- @Override
- public void onSubscriberAssociatedUriChanged(Uri[] uris) {
-
- }
- }
-
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@Override
diff --git a/telephony/java/com/android/internal/telephony/IRcs.aidl b/telephony/java/com/android/internal/telephony/IRcs.aidl
new file mode 100644
index 0000000..ede8695
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IRcs.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+interface IRcs {
+ void deleteThread(int threadId);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 006b040..dc23358 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,10 +38,12 @@
import android.telephony.SignalStrength;
import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.OperatorInfo;
@@ -1057,11 +1059,6 @@
*/
boolean isWifiCallingAvailable(int subId);
- /**
- * Returns the Status of VoLTE for the subscription ID specified.
- */
- boolean isVolteAvailable(int subId);
-
/**
* Returns the Status of VT (video telephony) for the subscription ID specified.
*/
@@ -1505,4 +1502,117 @@
*
*/
int getRadioPowerState(int slotIndex, String callingPackage);
+
+ // IMS specific AIDL commands, see ImsMmTelManager.java
+
+ /**
+ * Adds an IMS registration status callback for the subscription id specified.
+ */
+ oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ String callingPackage);
+ /**
+ * Removes an existing IMS registration status callback for the subscription specified.
+ */
+ oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+ String callingPackage);
+
+ /**
+ * Adds an IMS MmTel capabilities callback for the subscription specified.
+ */
+ oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ String callingPackage);
+
+ /**
+ * Removes an existing IMS MmTel capabilities callback for the subscription specified.
+ */
+ oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+ String callingPackage);
+
+ /**
+ * return true if the IMS MmTel capability for the given registration tech is capable.
+ */
+ boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+
+ /**
+ * return true if the IMS MmTel capability for the given registration tech is available.
+ */
+ boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+
+ /**
+ * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
+ */
+ boolean isAdvancedCallingSettingEnabled(int subId);
+
+ /**
+ * Modify the user's setting for whether or not 4G LTE is enabled.
+ */
+ void setAdvancedCallingSetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if the user's setting for VT is enabled for the subscription.
+ */
+ boolean isVtSettingEnabled(int subId, String callingPackage);
+
+ /**
+ * Modify the user's setting for whether or not VT is available for the subscrption specified.
+ */
+ void setVtSetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if the user's setting for whether or not Voice over WiFi is currently enabled.
+ */
+ boolean isVoWiFiSettingEnabled(int subId);
+
+ /**
+ * sets the user's setting for Voice over WiFi enabled state.
+ */
+ void setVoWiFiSetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if the user's setting for Voice over WiFi while roaming is enabled.
+ */
+ boolean isVoWiFiRoamingSettingEnabled(int subId);
+
+ /**
+ * Sets the user's preference for whether or not Voice over WiFi is enabled for the current
+ * subscription while roaming.
+ */
+ void setVoWiFiRoamingSetting(int subId, boolean isEnabled);
+
+ /**
+ * Set the Voice over WiFi enabled state, but do not persist the setting.
+ */
+ void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode);
+
+ /**
+ * return the Voice over WiFi mode preference set by the user for the subscription specified.
+ */
+ int getVoWiFiModeSetting(int subId);
+
+ /**
+ * sets the user's preference for the Voice over WiFi mode for the subscription specified.
+ */
+ void setVoWiFiModeSetting(int subId, int mode);
+
+ /**
+ * return the Voice over WiFi mode preference set by the user for the subscription specified
+ * while roaming.
+ */
+ int getVoWiFiRoamingModeSetting(int subId);
+
+ /**
+ * sets the user's preference for the Voice over WiFi mode for the subscription specified
+ * while roaming.
+ */
+ void setVoWiFiRoamingModeSetting(int subId, int mode);
+
+ /**
+ * Modify the user's setting for whether or not RTT is enabled for the subscrption specified.
+ */
+ void setRttCapabilitySetting(int subId, boolean isEnabled);
+
+ /**
+ * return true if TTY over VoLTE is enabled for the subscription specified.
+ */
+ boolean isTtyOverVolteEnabled(int subId);
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 976848c..eed8ae7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -92,7 +92,9 @@
"com.google.android.wearable.action.GOOGLE";
private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
- private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
+ private static final int BEFORE_FORCE_STOP_SLEEP_TIMEOUT = 1000; // 1s before force stopping
+ private static final int BEFORE_KILL_APP_SLEEP_TIMEOUT = 1000; // 1s before killing
+ private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 3000; // 3s between launching apps
private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
private static final String LAUNCH_FILE = "applaunch.txt";
@@ -327,7 +329,14 @@
}
}
if(mForceStopApp) {
- closeApp(launch.getApp());
+ sleep(BEFORE_FORCE_STOP_SLEEP_TIMEOUT);
+ forceStopApp(launch.getApp());
+ sleep(BEFORE_KILL_APP_SLEEP_TIMEOUT);
+ // Close again for good measure (just in case).
+ forceStopApp(launch.getApp());
+ // Kill the backgrounded process in the case forceStopApp only sent it to
+ // background.
+ killBackgroundApp(launch.getApp());
} else {
startHomeIntent();
}
@@ -638,7 +647,7 @@
// Kill all the apps
for (String appName : mNameToIntent.keySet()) {
Log.w(TAG, String.format("killing %s", appName));
- closeApp(appName);
+ forceStopApp(appName);
}
// Drop all the cache.
assertNotNull("Issue in dropping the cache",
@@ -646,7 +655,7 @@
.executeShellCommand(DROP_CACHE_SCRIPT));
}
- private void closeApp(String appName) {
+ private void forceStopApp(String appName) {
Intent startIntent = mNameToIntent.get(appName);
if (startIntent != null) {
String packageName = startIntent.getComponent().getPackageName();
@@ -658,6 +667,18 @@
}
}
+ private void killBackgroundApp(String appName) {
+ Intent startIntent = mNameToIntent.get(appName);
+ if (startIntent != null) {
+ String packageName = startIntent.getComponent().getPackageName();
+ try {
+ mAm.killBackgroundProcesses(packageName, UserHandle.USER_CURRENT);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error closing app", e);
+ }
+ }
+ }
+
private void sleep(int time) {
try {
Thread.sleep(time);
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 03a617c..6174c6c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -219,7 +219,7 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
- any(NetworkCapabilities.class), any(LinkProperties.class));
+ any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -247,7 +247,7 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
- any(NetworkCapabilities.class), any(LinkProperties.class));
+ any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
manager.unregisterNetworkCallback(callback);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c77fcc..17bcea0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,6 +51,10 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static com.android.internal.util.TestUtils.waitForIdleLooper;
@@ -92,6 +96,7 @@
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
@@ -148,6 +153,7 @@
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -215,11 +221,13 @@
private MockNetworkAgent mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
+ private INetworkPolicyListener mPolicyListener;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ @Mock INetworkPolicyManager mNpm;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -934,6 +942,11 @@
}
@Override
+ protected Tethering makeTethering() {
+ return mock(Tethering.class);
+ }
+
+ @Override
protected int reserveNetId() {
while (true) {
final int netId = super.reserveNetId();
@@ -1023,6 +1036,20 @@
public void waitForIdle() {
waitForIdle(TIMEOUT_MS);
}
+
+ public void setUidRulesChanged(int uidRules) {
+ try {
+ mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ public void setRestrictBackgroundChanged(boolean restrictBackground) {
+ try {
+ mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ } catch (RemoteException ignored) {
+ }
+ }
}
/**
@@ -1055,12 +1082,18 @@
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
+
mService = new WrappedConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
- mock(INetworkPolicyManager.class),
+ mNpm,
mock(IpConnectivityLog.class));
+ final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
+ ArgumentCaptor.forClass(INetworkPolicyListener.class);
+ verify(mNpm).registerListener(policyListenerCaptor.capture());
+ mPolicyListener = policyListenerCaptor.getValue();
+
// Create local CM before sending system ready so that we can answer
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
@@ -1441,7 +1474,8 @@
RESUMED,
LOSING,
LOST,
- UNAVAILABLE
+ UNAVAILABLE,
+ BLOCKED_STATUS
}
private static class CallbackInfo {
@@ -1522,6 +1556,11 @@
setLastCallback(CallbackState.LOST, network, null);
}
+ @Override
+ public void onBlockedStatusChanged(Network network, boolean blocked) {
+ setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+ }
+
public Network getLastAvailableNetwork() {
return mLastAvailableNetwork;
}
@@ -1582,6 +1621,7 @@
// - onSuspended, iff the network was suspended when the callbacks fire.
// - onCapabilitiesChanged.
// - onLinkPropertiesChanged.
+ // - onBlockedStatusChanged.
//
// @param agent the network to expect the callbacks on.
// @param expectSuspended whether to expect a SUSPENDED callback.
@@ -1589,7 +1629,7 @@
// onCapabilitiesChanged callback.
// @param timeoutMs how long to wait for the callbacks.
void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
- boolean expectValidated, int timeoutMs) {
+ boolean expectValidated, boolean expectBlocked, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1600,19 +1640,28 @@
expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
}
expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+ expectBlockedStatusCallback(expectBlocked, agent);
}
// Expects the available callbacks (validated), plus onSuspended.
void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
- expectAvailableCallbacks(agent, true, expectValidated, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
}
void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, true, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
}
void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, false, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
}
// Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1623,6 +1672,9 @@
expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
+ // Implicitly check the network is allowed to use.
+ // TODO: should we need to consider if network is in blocked status in this case?
+ expectBlockedStatusCallback(false, agent);
NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
assertEquals(nc1, nc2);
}
@@ -1665,6 +1717,12 @@
fn.test((NetworkCapabilities) cbi.arg));
}
+ void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
+ boolean actualBlocked = (boolean) cbi.arg;
+ assertEquals(expectBlocked, actualBlocked);
+ }
+
void assertNoCallback() {
waitForIdle();
CallbackInfo c = mCallbacks.peek();
@@ -3223,7 +3281,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
// pass timeout and validate that UNAVAILABLE is not called
@@ -3243,7 +3301,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3802,6 +3860,7 @@
networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
networkAgent);
+ networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
networkCallback.assertNoCallback();
checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
@@ -4010,6 +4069,7 @@
mCellNetworkAgent);
CallbackInfo cbi = cellNetworkCallback.expectCallback(
CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4068,6 +4128,7 @@
mCellNetworkAgent);
CallbackInfo cbi = cellNetworkCallback.expectCallback(
CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4444,6 +4505,101 @@
mMockVpn.disconnect();
}
+ @Test
+ public void testNetworkBlockedStatus() {
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build();
+ mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_REJECT_ALL);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+ // ConnectivityService should cache it not to invoke the callback again.
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ cellNetworkCallback.assertNoCallback();
+
+ mService.setUidRulesChanged(RULE_NONE);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+ // Restrict the network based on UID rule and NOT_METERED capability change.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+ mCellNetworkAgent);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ mService.setUidRulesChanged(RULE_ALLOW_METERED);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_NONE);
+ cellNetworkCallback.assertNoCallback();
+
+ // Restrict the network based on BackgroundRestricted.
+ mService.setRestrictBackgroundChanged(true);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ mService.setRestrictBackgroundChanged(true);
+ cellNetworkCallback.assertNoCallback();
+ mService.setRestrictBackgroundChanged(false);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ }
+
+ @Test
+ public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ // No Networkcallbacks invoked before any network is active.
+ mService.setUidRulesChanged(RULE_REJECT_ALL);
+ mService.setUidRulesChanged(RULE_NONE);
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ defaultCallback.assertNoCallback();
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+
+ // Allow to use the network after switching to NOT_METERED network.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Switch to METERED network. Restrict the use of the network.
+ mWiFiNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
+
+ // Network becomes NOT_METERED.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ // Verify there's no Networkcallbacks invoked after data saver on/off.
+ mService.setRestrictBackgroundChanged(true);
+ mService.setRestrictBackgroundChanged(false);
+ defaultCallback.assertNoCallback();
+
+ mCellNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
/**
* Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
*/