Merge "wifi: Move SAP passphrase ascii encodable check to wifi-service"
diff --git a/ApiDocs.bp b/ApiDocs.bp
index c82fee0f..faa0e5d 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -65,7 +65,7 @@
"test-base/src/**/*.java",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
+ ":art.module.public.api{.public.stubs.source}",
":conscrypt.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"test-mock/src/**/*.java",
@@ -135,7 +135,7 @@
],
knowntags: [
"docs/knowntags.txt",
- ":known-oj-tags",
+ ":art.module.public.api{.doctags}",
],
custom_template: "droiddoc-templates-sdk",
resourcesdir: "docs/html/reference/images/",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 852fcc6..a3a2094 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -47,7 +47,7 @@
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
+ ":art.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"**/package.html",
],
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index e83ea3a..ced2fb5 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,7 +1,10 @@
andrewlewis@google.com
aquilescanta@google.com
chz@google.com
+hdmoon@google.com
hkuang@google.com
+jinpark@google.com
+klhyun@google.com
lnilsson@google.com
marcone@google.com
sungsoo@google.com
diff --git a/api/current.txt b/api/current.txt
index 0c6670d..453d6f6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6138,6 +6138,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
+ field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000
field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000
field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000
@@ -16509,6 +16510,23 @@
package android.graphics.text {
+ public class GlyphStyle {
+ ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
+ ctor public GlyphStyle(@NonNull android.graphics.Paint);
+ method public void applyToPaint(@NonNull android.graphics.Paint);
+ method @ColorInt public int getColor();
+ method public int getFlags();
+ method @FloatRange(from=0) public float getFontSize();
+ method @FloatRange(from=0) public float getScaleX();
+ method @FloatRange(from=0) public float getSkewX();
+ method public void setColor(@ColorInt int);
+ method public void setFlags(int);
+ method public void setFontSize(@FloatRange(from=0) float);
+ method public void setFromPaint(@NonNull android.graphics.Paint);
+ method public void setScaleX(@FloatRange(from=0) float);
+ method public void setSkewX(@FloatRange(from=0) float);
+ }
+
public class LineBreaker {
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16569,6 +16587,25 @@
method @NonNull public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
}
+ public final class PositionedGlyphs {
+ method public float getAscent();
+ method public float getDescent();
+ method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
+ method public float getOriginX();
+ method public float getOriginY();
+ method public float getPositionX(@IntRange(from=0) int);
+ method public float getPositionY(@IntRange(from=0) int);
+ method @NonNull public android.graphics.text.GlyphStyle getStyle();
+ method public float getTotalAdvance();
+ method @IntRange(from=0) public int glyphCount();
+ }
+
+ public class TextShaper {
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ }
+
}
package android.hardware {
@@ -46799,6 +46836,7 @@
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -46993,6 +47031,10 @@
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+ field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+ field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+ field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
@@ -49994,6 +50036,10 @@
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
+ public class StyledTextShaper {
+ method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
+ }
+
public interface TextDirectionHeuristic {
method public boolean isRtl(char[], int, int);
method public boolean isRtl(CharSequence, int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index a058496..949a54a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7429,7 +7429,7 @@
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
- field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
+ field @Deprecated public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
@@ -7437,7 +7437,7 @@
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
- field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field @Deprecated public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
diff --git a/api/test-current.txt b/api/test-current.txt
index 75ea4b4..4c2aa5a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,6 +83,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method public long getTotalRam();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
@@ -1023,6 +1024,7 @@
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
@@ -3406,6 +3408,8 @@
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+ field public static final String SELECTED_SPELL_CHECKER_SUBTYPE = "selected_spell_checker_subtype";
field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
@@ -5289,6 +5293,7 @@
}
public interface WindowManager extends android.view.ViewManager {
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public default void holdLock(int);
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -5538,6 +5543,18 @@
}
+package android.view.textservice {
+
+ public final class SpellCheckerSubtype implements android.os.Parcelable {
+ field public static final int SUBTYPE_ID_NONE = 0; // 0x0
+ }
+
+ public final class TextServicesManager {
+ method public boolean isSpellCheckerEnabled();
+ }
+
+}
+
package android.widget {
public abstract class AbsListView extends android.widget.AdapterView<android.widget.ListAdapter> implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4ccc7e6..cb65482 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -499,7 +499,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10084
+ // Next: 10087
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -598,6 +598,7 @@
DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
GeneralExternalStorageAccessStats general_external_storage_access_stats =
10085 [(module) = "mediaprovider"];
+ IncomingSms incoming_sms = 10086 [(module) = "telephony"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3191,6 +3192,8 @@
optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event.
optional int32 left_boundary = 8; // left edge width + left inset
optional int32 right_boundary = 9; // screen width - (right edge width + right inset)
+ optional float ml_model_score = 10; // The score between 0 and 1 which is the prediction output
+ // for the Back Gesture model.
enum WindowHorizontalLocation {
DEFAULT_LOCATION = 0;
@@ -10461,6 +10464,59 @@
}
/**
+ * Pulls information for a single incoming SMS.
+ *
+ * Each pull creates multiple atoms, one for each SMS. The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message IncomingSms {
+ // Format of the SMS (3GPP or 3GPP2).
+ optional android.telephony.SmsFormatEnum sms_format = 1;
+
+ // Technology of the SMS (CS or IMS).
+ optional android.telephony.SmsTechEnum sms_tech = 2;
+
+ // Radio access technology (RAT) used for the SMS. It can be IWLAN in case of IMS.
+ optional android.telephony.NetworkTypeEnum rat = 3;
+
+ // Type the SMS.
+ optional android.telephony.SmsTypeEnum sms_type = 4;
+
+ // Number of total parts.
+ optional int32 total_parts = 5;
+
+ // Number of received parts (if smaller than total parts, the SMS was dropped).
+ optional int32 received_parts = 6;
+
+ // Indicates if the incoming SMS was blocked.
+ optional bool blocked = 7;
+
+ // Indicate a specific error handling the SMS
+ optional android.telephony.SmsIncomingErrorEnum error = 8;
+
+ // Whether the SMS was received while roaming.
+ optional bool is_roaming = 9;
+
+ // Index of the SIM is used, 0 for single-SIM devices.
+ optional int32 sim_slot_index = 10;
+
+ // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+ optional bool is_multi_sim = 11;
+
+ // Whether the message was received with an eSIM profile.
+ optional bool is_esim = 12;
+
+ // Carrier ID of the SIM card used for the SMS.
+ // See https://source.android.com/devices/tech/config/carrierid.
+ optional int32 carrier_id = 13;
+
+ // Random message ID.
+ optional int64 message_id = 14;
+}
+
+/**
* Logs gnss stats from location service provider
*
* Pulled from:
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 70ca49d..e75d2f6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4796,4 +4796,19 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * This is intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ public void holdLock(int durationMs) {
+ try {
+ getService().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 95bbebe..1a4db4e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -696,4 +696,10 @@
* @param enable set it to true to enable the app freezer, false to disable it.
*/
boolean enableAppFreezer(in boolean enable);
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * This is intended for use by the tests that need to imitate lock contention.
+ */
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cd352e1..e8937a8 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -19,12 +19,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -35,6 +39,7 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
+import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.IResultReceiver;
@@ -102,11 +107,20 @@
* FLAG_ONE_SHOT, <b>both</b> FLAG_ONE_SHOT and FLAG_NO_CREATE need to be supplied.
*/
public final class PendingIntent implements Parcelable {
+ private static final String TAG = "PendingIntent";
private final IIntentSender mTarget;
private IResultReceiver mCancelReceiver;
private IBinder mWhitelistToken;
private ArraySet<CancelListener> mCancelListeners;
+ /**
+ * It is now required to specify either {@link #FLAG_IMMUTABLE}
+ * or {@link #FLAG_MUTABLE} when creating a PendingIntent.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ static final long PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED = 160794467L;
+
/** @hide */
@IntDef(flag = true,
value = {
@@ -115,6 +129,7 @@
FLAG_CANCEL_CURRENT,
FLAG_UPDATE_CURRENT,
FLAG_IMMUTABLE,
+ FLAG_MUTABLE,
Intent.FILL_IN_ACTION,
Intent.FILL_IN_DATA,
@@ -175,6 +190,21 @@
public static final int FLAG_IMMUTABLE = 1<<26;
/**
+ * Flag indicating that the created PendingIntent should be mutable.
+ * This flag cannot be combined with {@link #FLAG_IMMUTABLE}. <p>Up until
+ * {@link android.os.Build.VERSION_CODES#R}, PendingIntents are assumed to
+ * be mutable by default, unless {@link #FLAG_IMMUTABLE} is set. Starting
+ * with {@link android.os.Build.VERSION_CODES#S}, it will be required to
+ * explicitly specify the mutability of PendingIntents on creation with
+ * either (@link #FLAG_IMMUTABLE} or {@link #FLAG_MUTABLE}. It is strongly
+ * recommended to use {@link #FLAG_IMMUTABLE} when creating a
+ * PendingIntent. {@link #FLAG_MUTABLE} should only be used when some
+ * functionality relies on modifying the underlying intent, e.g. any
+ * PendingIntent that needs to be used with inline reply or bubbles.
+ */
+ public static final int FLAG_MUTABLE = 1<<25;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -286,6 +316,27 @@
sOnMarshaledListener.set(listener);
}
+ private static void checkFlags(int flags, String packageName) {
+ final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
+ final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
+ String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
+ + " be specified when creating a PendingIntent.\nStrongly consider"
+ + " using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality"
+ + " depends on the PendingIntent being mutable, e.g. if it needs to"
+ + " be used with inline replies or bubbles.";
+
+ if (flagImmutableSet && flagMutableSet) {
+ throw new IllegalArgumentException(
+ "Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent");
+ }
+
+ if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
+ && !flagImmutableSet && !flagMutableSet) {
+ Log.e(TAG, msg);
+ }
+ }
+
/**
* Retrieve a PendingIntent that will start a new activity, like calling
* {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
@@ -350,6 +401,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -376,6 +428,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -495,6 +548,7 @@
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
+ checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -521,6 +575,7 @@
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
+ checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -572,6 +627,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
@@ -651,6 +707,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 24282365..ba894ae 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -795,4 +795,6 @@
boolean isAutoRevokeWhitelisted(String packageName);
void grantImplicitAccess(int queryingUid, String visibleAuthority);
+
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4eec56c..79e23b3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8305,4 +8305,19 @@
public static void uncorkPackageInfoCache() {
PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
}
+
+ /**
+ * Holds the PM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ public void holdLock(int durationMs) {
+ try {
+ ActivityThread.getPackageManager().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 1061121..0d0bfb3 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2175,10 +2175,8 @@
/**
* <p>The desired zoom ratio</p>
- * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} with dual purposes of crop and zoom, the
- * application can now choose to use this tag to specify the desired zoom level. The
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
- * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} for zoom, the application can now choose to
+ * use this tag to specify the desired zoom level.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
@@ -2860,11 +2858,18 @@
* respectively.</p>
* <p>The camera device may adjust the crop region to account for rounding and other hardware
* requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>The camera sensor output aspect ratio depends on factors such as output stream
+ * combination and {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}, and shouldn't be adjusted by using
+ * this control. And the camera device will treat different camera sensor output sizes
+ * (potentially with in-sensor crop) as the same crop of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}. As a result, the application shouldn't assume the
+ * maximum crop region always maps to the same aspect ratio or field of view for the
+ * sensor output.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
- * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * the capture request should be left as the default activeArray size. The
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
@@ -2874,6 +2879,7 @@
* capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 29a53fb..8cfa086 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2405,10 +2405,8 @@
/**
* <p>The desired zoom ratio</p>
- * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} with dual purposes of crop and zoom, the
- * application can now choose to use this tag to specify the desired zoom level. The
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
- * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} for zoom, the application can now choose to
+ * use this tag to specify the desired zoom level.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
@@ -3506,11 +3504,18 @@
* respectively.</p>
* <p>The camera device may adjust the crop region to account for rounding and other hardware
* requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>The camera sensor output aspect ratio depends on factors such as output stream
+ * combination and {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}, and shouldn't be adjusted by using
+ * this control. And the camera device will treat different camera sensor output sizes
+ * (potentially with in-sensor crop) as the same crop of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}. As a result, the application shouldn't assume the
+ * maximum crop region always maps to the same aspect ratio or field of view for the
+ * sensor output.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
- * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * the capture request should be left as the default activeArray size. The
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
@@ -3520,6 +3525,7 @@
* capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index f6cd726..1173c31 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -78,4 +78,26 @@
*/
public abstract boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken);
+
+ /** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
+ public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
+
+ /**
+ * Unregisters a {@link LidSwitchCallback callback} previously registered with
+ * {@link #registerLidSwitchCallback(LidSwitchCallback)}.
+ */
+ public abstract void unregisterLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
+
+ /** Callback interface for notifications relating to the lid switch. */
+ public interface LidSwitchCallback {
+ /**
+ * This callback is invoked when the lid switch changes state. Will be triggered once on
+ * registration of the callback with a {@code whenNanos} of 0 and then on every subsequent
+ * change in lid switch state.
+ *
+ * @param whenNanos the time when the change occurred
+ * @param lidOpen true if the lid is open
+ */
+ void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
+ }
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 52ee04c..54f80c6 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -267,6 +267,7 @@
}
if (appInfo == null) {
Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
+ return "";
}
final String abi = chooseAbi(appInfo);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 07867e2..e978608 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7663,6 +7663,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
/**
@@ -7673,6 +7675,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
"selected_spell_checker_subtype";
@@ -14464,6 +14468,15 @@
*/
public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
"nr_nsa_tracking_screen_off_mode";
+
+ /**
+ * Whether to show People Space.
+ * Values are:
+ * 0: Disabled (default)
+ * 1: Enabled
+ * @hide
+ */
+ public static final String SHOW_PEOPLE_SPACE = "show_people_space";
}
/**
diff --git a/core/java/android/text/StyledTextShaper.java b/core/java/android/text/StyledTextShaper.java
new file mode 100644
index 0000000..bf90614
--- /dev/null
+++ b/core/java/android/text/StyledTextShaper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextShaper;
+
+import java.util.List;
+
+/**
+ * Provides text shaping for multi-styled text.
+ *
+ * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint)
+ */
+public class StyledTextShaper {
+ private StyledTextShaper() {}
+
+
+ /**
+ * Shape multi-styled text.
+ *
+ * @param text a styled text.
+ * @param start a start index of shaping target in the text.
+ * @param count a length of shaping target in the text.
+ * @param dir a text direction.
+ * @param paint a paint
+ * @return a shape result.
+ */
+ public static @NonNull List<PositionedGlyphs> shapeText(
+ @NonNull CharSequence text, int start, int count,
+ @NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) {
+ MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
+ text, start, start + count, dir, null);
+ TextLine tl = TextLine.obtain();
+ try {
+ tl.set(paint, text, start, start + count,
+ mp.getParagraphDir(),
+ mp.getDirections(start, start + count),
+ false /* tabstop is not supported */,
+ null,
+ -1, -1 // ellipsis is not supported.
+ );
+ return tl.shape();
+ } finally {
+ TextLine.recycle(tl);
+ }
+ }
+
+}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 3c51fa7..b826832 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -23,6 +23,8 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextShaper;
import android.os.Build;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
@@ -35,6 +37,7 @@
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
+import java.util.List;
/**
* Represents a line of styled text, for measuring in visual order and
@@ -307,6 +310,36 @@
}
/**
+ * Shape the TextLine.
+ */
+ List<PositionedGlyphs> shape() {
+ List<PositionedGlyphs> glyphs = new ArrayList<>();
+ float horizontal = 0;
+ float x = 0;
+ final int runCount = mDirections.getRunCount();
+ for (int runIndex = 0; runIndex < runCount; runIndex++) {
+ final int runStart = mDirections.getRunStart(runIndex);
+ if (runStart > mLen) break;
+ final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
+ final boolean runIsRtl = mDirections.isRunRtl(runIndex);
+
+ int segStart = runStart;
+ for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
+ if (j == runLimit || charAt(j) == TAB_CHAR) {
+ horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal,
+ runIndex != (runCount - 1) || j != mLen);
+
+ if (j != runLimit) { // charAt(j) == TAB_CHAR
+ horizontal = mDir * nextTab(horizontal * mDir);
+ }
+ segStart = j + 1;
+ }
+ }
+ }
+ return glyphs;
+ }
+
+ /**
* Returns the signed graphical offset from the leading margin.
*
* Following examples are all for measuring offset=3. LX(e.g. L0, L1, ...) denotes a
@@ -483,12 +516,12 @@
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
float w = -measureRun(start, limit, limit, runIsRtl, null);
- handleRun(start, limit, limit, runIsRtl, c, x + w, top,
+ handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
y, bottom, null, false);
return w;
}
- return handleRun(start, limit, limit, runIsRtl, c, x, top,
+ return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
y, bottom, null, needWidth);
}
@@ -507,10 +540,35 @@
*/
private float measureRun(int start, int offset, int limit, boolean runIsRtl,
FontMetricsInt fmi) {
- return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
+ return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true);
}
/**
+ * Shape a unidirectional (but possibly multi-styled) run of text.
+ *
+ * @param glyphs the output positioned glyphs list
+ * @param start the line-relative start
+ * @param limit the line-relative limit
+ * @param runIsRtl true if the run is right-to-left
+ * @param x the position of the run that is closest to the leading margin
+ * @param needWidth true if the width value is required.
+ * @return the signed width of the run, based on the paragraph direction.
+ * Only valid if needWidth is true.
+ */
+ private float shapeRun(List<PositionedGlyphs> glyphs, int start,
+ int limit, boolean runIsRtl, float x, boolean needWidth) {
+
+ if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
+ float w = -measureRun(start, limit, limit, runIsRtl, null);
+ handleRun(start, limit, limit, runIsRtl, null, glyphs, x + w, 0, 0, 0, null, false);
+ return w;
+ }
+
+ return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth);
+ }
+
+
+ /**
* Walk the cursor through this line, skipping conjuncts and
* zero-width characters.
*
@@ -841,6 +899,7 @@
* @param end the end of the text
* @param runIsRtl true if the run is right-to-left
* @param c the canvas, can be null if rendering is not needed
+ * @param glyphs the output positioned glyph list, can be null if not necessary
* @param x the edge of the run closest to the leading margin
* @param top the top of the line
* @param y the baseline
@@ -854,7 +913,7 @@
*/
private float handleText(TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl,
- Canvas c, float x, int top, int y, int bottom,
+ Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom,
FontMetricsInt fmi, boolean needWidth, int offset,
@Nullable ArrayList<DecorationInfo> decorations) {
@@ -878,16 +937,20 @@
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
}
- if (c != null) {
- final float leftX, rightX;
- if (runIsRtl) {
- leftX = x - totalWidth;
- rightX = x;
- } else {
- leftX = x;
- rightX = x + totalWidth;
- }
+ final float leftX, rightX;
+ if (runIsRtl) {
+ leftX = x - totalWidth;
+ rightX = x;
+ } else {
+ leftX = x;
+ rightX = x + totalWidth;
+ }
+ if (glyphs != null) {
+ shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
+ }
+
+ if (c != null) {
if (wp.bgColor != 0) {
int previousColor = wp.getColor();
Paint.Style previousStyle = wp.getStyle();
@@ -1072,6 +1135,7 @@
* @param limit the limit of the run
* @param runIsRtl true if the run is right-to-left
* @param c the canvas, can be null
+ * @param glyphs the output positioned glyphs, can be null
* @param x the end of the run closest to the leading margin
* @param top the top of the line
* @param y the baseline
@@ -1082,7 +1146,8 @@
* valid if needWidth is true
*/
private float handleRun(int start, int measureLimit,
- int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
+ int limit, boolean runIsRtl, Canvas c,
+ List<PositionedGlyphs> glyphs, float x, int top, int y,
int bottom, FontMetricsInt fmi, boolean needWidth) {
if (measureLimit < start || measureLimit > limit) {
@@ -1115,7 +1180,7 @@
wp.set(mPaint);
wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
- return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
+ return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top,
y, bottom, fmi, needWidth, measureLimit, null);
}
@@ -1196,8 +1261,8 @@
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
activePaint.setEndHyphenEdit(
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
- x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
- top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
+ x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
+ glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
activeStart = j;
@@ -1223,7 +1288,7 @@
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
activePaint.setEndHyphenEdit(
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
- x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
+ x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x,
top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
}
@@ -1260,6 +1325,45 @@
}
/**
+ * Shape a text run with the set-up paint.
+ *
+ * @param glyphs the output positioned glyphs list
+ * @param paint the paint used to render the text
+ * @param start the start of the run
+ * @param end the end of the run
+ * @param contextStart the start of context for the run
+ * @param contextEnd the end of the context for the run
+ * @param runIsRtl true if the run is right-to-left
+ * @param x the x position of the left edge of the run
+ */
+ private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint,
+ int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) {
+
+ int count = end - start;
+ int contextCount = contextEnd - contextStart;
+ if (mCharsValid) {
+ glyphs.add(TextShaper.shapeTextRun(
+ mChars,
+ start, count,
+ contextStart, contextCount,
+ x, 0f,
+ runIsRtl,
+ paint
+ ));
+ } else {
+ glyphs.add(TextShaper.shapeTextRun(
+ mText,
+ mStart + start, count,
+ mStart + contextStart, contextCount,
+ x, 0f,
+ runIsRtl,
+ paint
+ ));
+ }
+ }
+
+
+ /**
* Returns the next tab position.
*
* @param h the (unsigned) offset from the leading margin
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
new file mode 100644
index 0000000..865d560
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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.util.imetracing;
+
+import android.app.ActivityThread;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.view.IInputMethodManager;
+
+/**
+ *
+ * An abstract class that declares the methods for ime trace related operations - enable trace,
+ * schedule trace and add new trace to buffer. Both the client and server side classes can use
+ * it by getting an implementation through {@link ImeTracing#getInstance()}.
+ *
+ * @hide
+ */
+public abstract class ImeTracing {
+
+ static final String TAG = "imeTracing";
+ public static final String PROTO_ARG = "--proto-com-android-imetracing";
+
+ private static ImeTracing sInstance;
+ static boolean sEnabled = false;
+ IInputMethodManager mService;
+
+ ImeTracing() throws ServiceNotFoundException {
+ mService = IInputMethodManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+ }
+
+ /**
+ * Returns an instance of {@link ImeTracingServerImpl} when called from a server side class
+ * and an instance of {@link ImeTracingClientImpl} when called from a client side class.
+ * Useful to schedule a dump for next frame or save a dump when certain methods are called.
+ *
+ * @return Instance of one of the children classes of {@link ImeTracing}
+ */
+ public static ImeTracing getInstance() {
+ if (sInstance == null) {
+ try {
+ sInstance = isSystemProcess()
+ ? new ImeTracingServerImpl() : new ImeTracingClientImpl();
+ } catch (RemoteException | ServiceNotFoundException e) {
+ Log.e(TAG, "Exception while creating ImeTracing instance", e);
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Sends request to start proto dump to {@link ImeTracingServerImpl} when called from a
+ * server process and to {@link ImeTracingClientImpl} when called from a client process.
+ */
+ public abstract void triggerDump();
+
+ /**
+ * @param proto dump to be added to the buffer
+ */
+ public abstract void addToBuffer(ProtoOutputStream proto);
+
+ /**
+ * @param shell The shell command to process
+ * @return {@code 0} if the command was successfully processed, {@code -1} otherwise
+ */
+ public abstract int onShellCommand(ShellCommand shell);
+
+ /**
+ * Sets whether ime tracing is enabled.
+ *
+ * @param enabled Tells whether ime tracing should be enabled or disabled.
+ */
+ public void setEnabled(boolean enabled) {
+ sEnabled = enabled;
+ }
+
+ /**
+ * @return {@code true} if dumping is enabled, {@code false} otherwise.
+ */
+ public boolean isEnabled() {
+ return sEnabled;
+ }
+
+ /**
+ * @return {@code true} if tracing is available, {@code false} otherwise.
+ */
+ public boolean isAvailable() {
+ return mService != null;
+ }
+
+ private static boolean isSystemProcess() {
+ return ActivityThread.isSystem();
+ }
+}
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
new file mode 100644
index 0000000..e5d7d338
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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.util.imetracing;
+
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.InputMethodManager;
+
+/**
+ * @hide
+ */
+class ImeTracingClientImpl extends ImeTracing {
+
+ private boolean mDumpInProgress;
+ private final Object mDumpInProgressLock = new Object();
+
+ ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
+ sEnabled = mService.isImeTraceEnabled();
+ }
+
+ @Override
+ public void addToBuffer(ProtoOutputStream proto) {
+ }
+
+ @Override
+ public int onShellCommand(ShellCommand shell) {
+ return -1;
+ }
+
+ @Override
+ public void triggerDump() {
+ if (isAvailable() && isEnabled()) {
+ boolean doDump = false;
+ synchronized (mDumpInProgressLock) {
+ if (!mDumpInProgress) {
+ mDumpInProgress = true;
+ doDump = true;
+ }
+ }
+
+ if (doDump) {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ InputMethodManager.dumpProto(proto);
+ mService.startProtoDump(proto.getBytes());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while sending ime-related client dump to server", e);
+ } finally {
+ mDumpInProgress = false;
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
new file mode 100644
index 0000000..350cf57
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 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.util.imetracing;
+
+import static android.os.Build.IS_USER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER_H;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER_L;
+
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @hide
+ */
+class ImeTracingServerImpl extends ImeTracing {
+ private static final String TRACE_FILENAME = "/data/misc/wmtrace/ime_trace.pb";
+ private static final int BUFFER_CAPACITY = 4096 * 1024;
+
+ // Needed for winscope to auto-detect the dump type. Explained further in
+ // core.proto.android.view.inputmethod.inputmethodeditortrace.proto
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final TraceBuffer mBuffer;
+ private final File mTraceFile;
+ private final Object mEnabledLock = new Object();
+
+ ImeTracingServerImpl() throws ServiceNotFoundException {
+ mBuffer = new TraceBuffer<>(BUFFER_CAPACITY);
+ mTraceFile = new File(TRACE_FILENAME);
+ }
+
+ /**
+ * The provided dump is added to the current dump buffer {@link ImeTracingServerImpl#mBuffer}.
+ *
+ * @param proto dump to be added to the buffer
+ */
+ @Override
+ public void addToBuffer(ProtoOutputStream proto) {
+ if (isAvailable() && isEnabled()) {
+ mBuffer.add(proto);
+ }
+ }
+
+ /**
+ * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>"
+ *
+ * @param shell The shell command to process
+ * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise
+ */
+ @Override
+ public int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ String cmd = shell.getNextArgRequired();
+ switch (cmd) {
+ case "start":
+ startTrace(pw);
+ return 0;
+ case "stop":
+ stopTrace(pw);
+ return 0;
+ default:
+ pw.println("Unknown command: " + cmd);
+ pw.println("Input method trace options:");
+ pw.println(" start: Start tracing");
+ pw.println(" stop: Stop tracing");
+ return -1;
+ }
+ }
+
+ @Override
+ public void triggerDump() {
+ if (isAvailable() && isEnabled()) {
+ try {
+ mService.startProtoDump(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while triggering proto dump", e);
+ }
+ }
+ }
+
+ private void writeTraceToFileLocked() {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+
+ @GuardedBy("mEnabledLock")
+ private void startTrace(PrintWriter pw) {
+ if (IS_USER) {
+ Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+ return;
+ }
+
+ synchronized (mEnabledLock) {
+ if (isAvailable() && isEnabled()) {
+ Log.w(TAG, "Warn: Tracing is already started.");
+ return;
+ }
+
+ pw.println("Starting tracing to " + mTraceFile + ".");
+ sEnabled = true;
+ mBuffer.resetBuffer();
+ }
+ }
+
+ @GuardedBy("mEnabledLock")
+ private void stopTrace(PrintWriter pw) {
+ if (IS_USER) {
+ Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+ return;
+ }
+
+ synchronized (mEnabledLock) {
+ if (!isAvailable() || !isEnabled()) {
+ Log.w(TAG, "Warn: Tracing is not available or not started.");
+ return;
+ }
+
+ pw.println("Stopping tracing and writing traces to " + mTraceFile + ".");
+ sEnabled = false;
+ writeTraceToFileLocked();
+ mBuffer.resetBuffer();
+ }
+ }
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5836538..daab70a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -752,4 +752,10 @@
*/
void requestScrollCapture(int displayId, IBinder behindClient, int taskId,
IScrollCaptureController controller);
+
+ /**
+ * Holds the WM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ */
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 92772c1..efc0bd2 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -16,16 +16,23 @@
package android.view;
+import static android.view.ImeFocusControllerProto.HAS_IME_FOCUS;
+import static android.view.ImeFocusControllerProto.NEXT_SERVED_VIEW;
+import static android.view.ImeFocusControllerProto.SERVED_VIEW;
+
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.UiThread;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
+import java.util.Objects;
+
/**
* Responsible for IME focus handling inside {@link ViewRootImpl}.
* @hide
@@ -280,4 +287,12 @@
boolean hasImeFocus() {
return mHasImeFocus;
}
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HAS_IME_FOCUS, mHasImeFocus);
+ proto.write(SERVED_VIEW, Objects.toString(mServedView));
+ proto.write(NEXT_SERVED_VIEW, Objects.toString(mNextServedView));
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 82f6036..dd1a194 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -16,6 +16,9 @@
package android.view;
+import static android.view.ImeInsetsSourceConsumerProto.FOCUSED_EDITOR;
+import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
+import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.ITYPE_IME;
@@ -24,6 +27,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
@@ -111,7 +115,6 @@
public @ShowResult int requestShow(boolean fromIme) {
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
-
if (getControl() == null) {
// If control is null, schedule to show IME when control is available.
mIsRequestedVisibleAwaitingControl = true;
@@ -227,6 +230,17 @@
return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
}
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
+ if (mFocusedEditor != null) {
+ mFocusedEditor.dumpDebug(proto, FOCUSED_EDITOR);
+ }
+ proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
+ proto.end(token);
+ }
+
private InputMethodManager getImm() {
return mController.getHost().getInputMethodManager();
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6ffd892..71899fa 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -17,6 +17,14 @@
package android.view;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED;
+import static android.view.InsetsAnimationControlImplProto.IS_FINISHED;
+import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION;
+import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
+import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
+import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
@@ -38,6 +46,8 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -48,6 +58,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Implements {@link WindowInsetsAnimationController}
@@ -122,6 +133,10 @@
mAnimationType = animationType;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
+
+ if ((mTypes & WindowInsets.Type.ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
}
private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
@@ -285,6 +300,20 @@
return mAnimation;
}
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(IS_CANCELLED, mCancelled);
+ proto.write(IS_FINISHED, mFinished);
+ proto.write(TMP_MATRIX, Objects.toString(mTmpMatrix));
+ proto.write(PENDING_INSETS, Objects.toString(mPendingInsets));
+ proto.write(PENDING_FRACTION, mPendingFraction);
+ proto.write(SHOWN_ON_FINISH, mShownOnFinish);
+ proto.write(CURRENT_ALPHA, mCurrentAlpha);
+ proto.write(PENDING_ALPHA, mPendingAlpha);
+ proto.end(token);
+ }
+
WindowInsetsAnimationControlListener getListener() {
return mListener;
}
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index 0711c3e..0275b52 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -16,6 +16,7 @@
package android.view;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
@@ -53,4 +54,14 @@
* @return The animation type this runner is running.
*/
@AnimationType int getAnimationType();
+
+ /**
+ *
+ * Export the state of classes that implement this interface into a protocol buffer
+ * output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of the implementation class
+ */
+ void dumpDebug(ProtoOutputStream proto, long fieldId);
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 1236044..cc3cd27 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -25,6 +25,7 @@
import android.os.Trace;
import android.util.Log;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -122,6 +123,12 @@
@Override
@UiThread
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ mControl.dumpDebug(proto, fieldId);
+ }
+
+ @Override
+ @UiThread
public int getTypes() {
return mControl.getTypes();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b438f8f..652781a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsControllerProto.CONTROL;
+import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.toInternalType;
@@ -41,6 +43,8 @@
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
@@ -298,6 +302,10 @@
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
+
mController = controller;
if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
@@ -812,6 +820,9 @@
@VisibleForTesting
public void show(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
// Handle pending request ready in case there was one set.
if (fromIme && mPendingImeControlRequest != null) {
PendingControlRequest pendingRequest = mPendingImeControlRequest;
@@ -860,6 +871,9 @@
}
void hide(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -894,6 +908,9 @@
listener.onCancelled(null);
return;
}
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
interpolator, animationType, getLayoutInsetsDuringAnimationMode(types),
@@ -1292,6 +1309,9 @@
private void hideDirectly(
@InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
@@ -1299,6 +1319,9 @@
}
private void showDirectly(@InsetsType int types) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
@@ -1318,6 +1341,16 @@
mState.dump(prefix + " ", pw);
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mState.dumpDebug(proto, STATE);
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner;
+ runner.dumpDebug(proto, CONTROL);
+ }
+ proto.end(token);
+ }
+
@VisibleForTesting
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index dbf7570..41cc8459 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,10 @@
package android.view;
+import static android.view.InsetsSourceProto.FRAME;
+import static android.view.InsetsSourceProto.TYPE;
+import static android.view.InsetsSourceProto.VISIBLE;
+import static android.view.InsetsSourceProto.VISIBLE_FRAME;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -25,6 +29,7 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
@@ -183,6 +188,17 @@
return false;
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(TYPE, InsetsState.typeToString(mType));
+ mFrame.dumpDebug(proto, FRAME);
+ if (mVisibleFrame != null) {
+ mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
+ }
+ proto.write(VISIBLE, mVisible);
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ba40459..d7ceaf7 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -19,6 +19,13 @@
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
+import static android.view.InsetsSourceConsumerProto.HAS_WINDOW_FOCUS;
+import static android.view.InsetsSourceConsumerProto.INTERNAL_INSETS_TYPE;
+import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
+import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
+import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
+import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
@@ -28,6 +35,8 @@
import android.annotation.Nullable;
import android.graphics.Rect;
import android.util.Log;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
@@ -319,6 +328,9 @@
@VisibleForTesting(visibility = PACKAGE)
public boolean notifyAnimationFinished() {
+ if (mType == ITYPE_IME) {
+ ImeTracing.getInstance().triggerDump();
+ }
if (mPendingFrame != null) {
InsetsSource source = mState.getSource(mType);
source.setFrame(mPendingFrame);
@@ -360,4 +372,21 @@
t.apply();
onPerceptible(mRequestedVisible);
}
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(INTERNAL_INSETS_TYPE, InsetsState.typeToString(mType));
+ proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
+ proto.write(IS_REQUESTED_VISIBLE, mRequestedVisible);
+ if (mSourceControl != null) {
+ mSourceControl.dumpDebug(proto, SOURCE_CONTROL);
+ }
+ if (mPendingFrame != null) {
+ mPendingFrame.dumpDebug(proto, PENDING_FRAME);
+ }
+ if (mPendingVisibleFrame != null) {
+ mPendingVisibleFrame.dumpDebug(proto, PENDING_VISIBLE_FRAME);
+ }
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 51b4921..b45bd38 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -16,10 +16,17 @@
package android.view;
+import static android.graphics.PointProto.X;
+import static android.graphics.PointProto.Y;
+import static android.view.InsetsSourceControlProto.LEASH;
+import static android.view.InsetsSourceControlProto.POSITION;
+import static android.view.InsetsSourceControlProto.TYPE;
+
import android.annotation.Nullable;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
@@ -120,4 +127,19 @@
return new InsetsSourceControl[size];
}
};
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(TYPE, InsetsState.typeToString(mType));
+
+ final long surfaceToken = proto.start(POSITION);
+ proto.write(X, mSurfacePosition.x);
+ proto.write(Y, mSurfacePosition.y);
+ proto.end(surfaceToken);
+
+ if (mLeash != null) {
+ mLeash.dumpDebug(proto, LEASH);
+ }
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c5d0a10..eabb718 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsStateProto.DISPLAY_FRAME;
+import static android.view.InsetsStateProto.SOURCES;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
@@ -41,6 +43,7 @@
import android.os.Parcelable;
import android.util.ArraySet;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -545,6 +548,16 @@
}
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ InsetsSource source = mSources[ITYPE_IME];
+ if (source != null) {
+ source.dumpDebug(proto, SOURCES);
+ }
+ mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
+ proto.end(token);
+ }
+
public static String typeToString(@InternalInsetsType int type) {
switch (type) {
case ITYPE_STATUS_BAR:
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7452d64f..778ebb0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -33,6 +33,23 @@
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImplProto.ADDED;
+import static android.view.ViewRootImplProto.APP_VISIBLE;
+import static android.view.ViewRootImplProto.CUR_SCROLL_Y;
+import static android.view.ViewRootImplProto.DISPLAY_ID;
+import static android.view.ViewRootImplProto.HEIGHT;
+import static android.view.ViewRootImplProto.IS_ANIMATING;
+import static android.view.ViewRootImplProto.IS_DRAWING;
+import static android.view.ViewRootImplProto.LAST_WINDOW_INSETS;
+import static android.view.ViewRootImplProto.PENDING_DISPLAY_CUTOUT;
+import static android.view.ViewRootImplProto.REMOVED;
+import static android.view.ViewRootImplProto.SCROLL_Y;
+import static android.view.ViewRootImplProto.SOFT_INPUT_MODE;
+import static android.view.ViewRootImplProto.VIEW;
+import static android.view.ViewRootImplProto.VISIBLE_RECT;
+import static android.view.ViewRootImplProto.WIDTH;
+import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
+import static android.view.ViewRootImplProto.WIN_FRAME;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -61,6 +78,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_FOCUS_CONTROLLER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INSETS_CONTROLLER;
import android.Manifest;
import android.animation.LayoutTransition;
@@ -127,6 +146,8 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface.OutOfResourcesException;
@@ -164,6 +185,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.DecorView;
@@ -182,6 +204,7 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
@@ -1787,7 +1810,7 @@
.setParent(getRenderSurfaceControl())
.setCallsite("ViewRootImpl.getBoundsLayer")
.build();
- setBoundsLayerCrop();
+ setBoundsLayerCrop(mTransaction);
mTransaction.show(mBoundsLayer).apply();
}
return mBoundsLayer;
@@ -1815,25 +1838,41 @@
return ret;
}
- private void setBoundsLayerCrop() {
+ private void setBoundsLayerCrop(Transaction t) {
// mWinFrame is already adjusted for surface insets. So offset it and use it as
// the cropping bounds.
mTempBoundsRect.set(mWinFrame);
mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
mWindowAttributes.surfaceInsets.top);
- mTransaction.setWindowCrop(mBoundsLayer, mTempBoundsRect);
+ t.setWindowCrop(mBoundsLayer, mTempBoundsRect);
}
/**
* Called after window layout to update the bounds surface. If the surface insets have changed
* or the surface has resized, update the bounds surface.
*/
- private void updateBoundsLayer() {
+ private boolean updateBoundsLayer(SurfaceControl.Transaction t) {
if (mBoundsLayer != null) {
- setBoundsLayerCrop();
- mTransaction.deferTransactionUntil(mBoundsLayer,
- getRenderSurfaceControl(), mSurface.getNextFrameNumber())
- .apply();
+ setBoundsLayerCrop(t);
+ t.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+ mSurface.getNextFrameNumber());
+ return true;
+ }
+ return false;
+ }
+
+ private void prepareSurfaces(boolean sizeChanged) {
+ final SurfaceControl.Transaction t = mTransaction;
+ final SurfaceControl sc = getRenderSurfaceControl();
+ if (!sc.isValid()) return;
+
+ boolean applyTransaction = updateBoundsLayer(t);
+ if (sizeChanged) {
+ applyTransaction = true;
+ t.setBufferSize(sc, mSurfaceSize.x, mSurfaceSize.y);
+ }
+ if (applyTransaction) {
+ t.apply();
}
}
@@ -2924,7 +2963,16 @@
}
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
- updateBoundsLayer();
+ // If the surface has been replaced, there's a chance the bounds layer is not parented
+ // to the new layer. When updating bounds layer, also reparent to the main VRI
+ // SurfaceControl to ensure it's correctly placed in the hierarchy.
+ //
+ // This needs to be done on the client side since WMS won't reparent the children to the
+ // new surface if it thinks the app is closing. WMS gets the signal that the app is
+ // stopping, but on the client side it doesn't get stopped since it's restarted quick
+ // enough. WMS doesn't want to keep around old children since they will leak when the
+ // client creates new children.
+ prepareSurfaces(surfaceSizeChanged);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -7517,6 +7565,38 @@
mView.debug();
}
+ /**
+ * Export the state of {@link ViewRootImpl} and other relevant classes into a protocol buffer
+ * output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of ViewRootImpl as defined in the parent message
+ */
+ @GuardedBy("this")
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(VIEW, Objects.toString(mView));
+ proto.write(DISPLAY_ID, mDisplay.getDisplayId());
+ proto.write(APP_VISIBLE, mAppVisible);
+ proto.write(HEIGHT, mHeight);
+ proto.write(WIDTH, mWidth);
+ proto.write(IS_ANIMATING, mIsAnimating);
+ mVisRect.dumpDebug(proto, VISIBLE_RECT);
+ proto.write(IS_DRAWING, mIsDrawing);
+ proto.write(ADDED, mAdded);
+ mWinFrame.dumpDebug(proto, WIN_FRAME);
+ mPendingDisplayCutout.get().dumpDebug(proto, PENDING_DISPLAY_CUTOUT);
+ proto.write(LAST_WINDOW_INSETS, Objects.toString(mLastWindowInsets));
+ proto.write(SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(mSoftInputMode));
+ proto.write(SCROLL_Y, mScrollY);
+ proto.write(CUR_SCROLL_Y, mCurScrollY);
+ proto.write(REMOVED, mRemoved);
+ mWindowAttributes.dumpDebug(proto, WINDOW_ATTRIBUTES);
+ proto.end(token);
+ mInsetsController.dumpDebug(proto, INSETS_CONTROLLER);
+ mImeFocusController.dumpDebug(proto, IME_FOCUS_CONTROLLER);
+ }
+
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
String innerPrefix = prefix + " ";
writer.println(prefix + "ViewRoot:");
@@ -9080,6 +9160,9 @@
@Override
public void showInsets(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.showInsets(types, fromIme);
@@ -9088,6 +9171,9 @@
@Override
public void hideInsets(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.hideInsets(types, fromIme);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0d62da6..e96e98b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3994,4 +3994,15 @@
}
}
}
+
+ /**
+ * Holds the WM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ default void holdLock(int durationMs) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f57ee65..59e0226 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -281,4 +281,13 @@
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 104bc43..7dbf693 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -17,6 +17,12 @@
package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.inputmethod.EditorInfoProto.FIELD_ID;
+import static android.view.inputmethod.EditorInfoProto.IME_OPTIONS;
+import static android.view.inputmethod.EditorInfoProto.INPUT_TYPE;
+import static android.view.inputmethod.EditorInfoProto.PACKAGE_NAME;
+import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS;
+import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -32,6 +38,7 @@
import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -795,6 +802,26 @@
}
/**
+ * Export the state of {@link EditorInfo} into a protocol buffer output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of ViewRootImpl as defined in the parent message
+ * @hide
+ */
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(INPUT_TYPE, inputType);
+ proto.write(IME_OPTIONS, imeOptions);
+ proto.write(PRIVATE_IME_OPTIONS, privateImeOptions);
+ proto.write(PACKAGE_NAME, packageName);
+ proto.write(FIELD_ID, this.fieldId);
+ if (targetInputMethodUser != null) {
+ proto.write(TARGET_INPUT_METHOD_USER_ID, targetInputMethodUser.getIdentifier());
+ }
+ proto.end(token);
+ }
+
+ /**
* Write debug output of this object.
*/
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 8adb7e5..b8f04159 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,6 +18,17 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.util.imetracing.ImeTracing.PROTO_ARG;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.DISPLAY_ID;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.EDITOR_INFO;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INPUT_METHOD_MANAGER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.VIEW_ROOT_IMPL;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientsProto.CLIENT;
+import static android.view.inputmethod.InputMethodManagerProto.ACTIVE;
+import static android.view.inputmethod.InputMethodManagerProto.CUR_ID;
+import static android.view.inputmethod.InputMethodManagerProto.FULLSCREEN_MODE;
+import static android.view.inputmethod.InputMethodManagerProto.SERVED_CONNECTING;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION;
@@ -62,6 +73,8 @@
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.ImeFocusController;
import android.view.ImeInsetsSourceConsumer;
@@ -564,6 +577,7 @@
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags) {
final View servedView;
+ ImeTracing.getInstance().triggerDump();
synchronized (mH) {
mCurrentTextBoxAttribute = null;
mCompletions = null;
@@ -1084,6 +1098,11 @@
mH.obtainMessage(MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX, bindSequence, 0,
matrixValues).sendToTarget();
}
+
+ @Override
+ public void setImeTraceEnabled(boolean enabled) {
+ ImeTracing.getInstance().setEnabled(enabled);
+ }
};
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1652,6 +1671,7 @@
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+ ImeTracing.getInstance().triggerDump();
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
@@ -1757,6 +1777,7 @@
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
+ ImeTracing.getInstance().triggerDump();
checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
@@ -3108,6 +3129,10 @@
}
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (processDump(fd, args)) {
+ return;
+ }
+
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
@@ -3202,4 +3227,74 @@
return sb.toString();
}
+
+ /**
+ * Checks the args to see if a proto-based ime dump was requested and writes the client side
+ * ime dump to the given {@link FileDescriptor}.
+ *
+ * @return {@code true} if a proto-based ime dump was requested.
+ */
+ private boolean processDump(final FileDescriptor fd, final String[] args) {
+ if (args == null) {
+ return false;
+ }
+
+ for (String arg : args) {
+ if (arg.equals(PROTO_ARG)) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ dumpProto(proto);
+ proto.flush();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Write the proto dump for all displays associated with this client.
+ *
+ * @param proto The proto stream to which the dumps are written.
+ * @hide
+ */
+ public static void dumpProto(ProtoOutputStream proto) {
+ for (int i = sInstanceMap.size() - 1; i >= 0; i--) {
+ InputMethodManager imm = sInstanceMap.valueAt(i);
+ imm.dumpDebug(proto);
+ }
+ }
+
+ /**
+ * Write the proto dump of various client side components to the provided
+ * {@link ProtoOutputStream}.
+ *
+ * @param proto The proto stream to which the dumps are written.
+ * @hide
+ */
+ @GuardedBy("mH")
+ public void dumpDebug(ProtoOutputStream proto) {
+ if (mCurMethod == null) {
+ return;
+ }
+
+ final long clientDumpToken = proto.start(CLIENT);
+ proto.write(DISPLAY_ID, mDisplayId);
+ final long token = proto.start(INPUT_METHOD_MANAGER);
+ synchronized (mH) {
+ proto.write(CUR_ID, mCurId);
+ proto.write(FULLSCREEN_MODE, mFullscreenMode);
+ proto.write(ACTIVE, mActive);
+ proto.write(SERVED_CONNECTING, mServedConnecting);
+ proto.end(token);
+ if (mCurRootView != null) {
+ mCurRootView.dumpDebug(proto, VIEW_ROOT_IMPL);
+ }
+ if (mCurrentTextBoxAttribute != null) {
+ mCurrentTextBoxAttribute.dumpDebug(proto, EDITOR_INFO);
+ }
+ if (mImeInsetsConsumer != null) {
+ mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER);
+ }
+ }
+ proto.end(clientDumpToken);
+ }
}
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index 8224e0e..12fe626 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
@@ -53,6 +54,7 @@
/**
* @hide
*/
+ @TestApi
public static final int SUBTYPE_ID_NONE = 0;
private static final String SUBTYPE_LANGUAGE_TAG_NONE = "";
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index acb35d6..cd70a31 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -259,6 +260,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public boolean isSpellCheckerEnabled() {
try {
return mService.isSpellCheckerEnabled(mUserId);
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 4509032..c9443b0 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -33,4 +33,5 @@
void reportPreRendered(in EditorInfo info);
void applyImeVisibility(boolean setVisible);
void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues);
+ void setImeTraceEnabled(boolean enabled);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index a1cbd3f..5a06273 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -77,4 +77,6 @@
void removeImeSurface();
/** Remove the IME surface. Requires passing the currently focused window. */
void removeImeSurfaceFromWindow(in IBinder windowToken);
+ void startProtoDump(in byte[] clientProtoDump);
+ boolean isImeTraceEnabled();
}
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index f14e3ed..b56bd2b 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -159,3 +159,50 @@
*/
SIM_STATE_PRESENT = 11;
}
+
+// Format of SMS message
+enum SmsFormatEnum {
+ /** Unknown format */
+ SMS_FORMAT_UNKNOWN = 0;
+ /** Format compliant with 3GPP TS 23.040 */
+ SMS_FORMAT_3GPP = 1;
+ /** Format compliant with 3GPP2 TS C.S0015-B */
+ SMS_FORMAT_3GPP2 = 2;
+}
+
+// Technology used to carry an SMS message
+enum SmsTechEnum {
+ /**
+ * Unknown SMS technology used to carry the SMS.
+ * This value is also used for injected SMS.
+ */
+ SMS_TECH_UNKNOWN = 0;
+ /** The SMS was carried over CS bearer in 3GPP network */
+ SMS_TECH_CS_3GPP = 1;
+ /** The SMS was carried over CS bearer in 3GPP2 network */
+ SMS_TECH_CS_3GPP2 = 2;
+ /** The SMS was carried over IMS */
+ SMS_TECH_IMS = 3;
+}
+
+// Types of SMS message
+enum SmsTypeEnum {
+ /** Normal type. */
+ SMS_TYPE_NORMAL = 0;
+ /** SMS-PP (point-to-point). */
+ SMS_TYPE_SMS_PP = 1;
+ /** Voicemail indication. */
+ SMS_TYPE_VOICEMAIL_INDICATION = 2;
+ /** Type 0 message (3GPP TS 23.040 9.2.3.9). */
+ SMS_TYPE_ZERO = 3;
+ /** WAP-PUSH message. */
+ SMS_TYPE_WAP_PUSH = 4;
+}
+
+// SMS errors
+enum SmsIncomingErrorEnum {
+ SMS_SUCCESS = 0;
+ SMS_ERROR_GENERIC = 1;
+ SMS_ERROR_NO_MEMORY = 2;
+ SMS_ERROR_NOT_SUPPORTED = 3;
+}
diff --git a/core/proto/android/view/imeinsetssourceconsumer.proto b/core/proto/android/view/imeinsetssourceconsumer.proto
index 6809163..5bee81b 100644
--- a/core/proto/android/view/imeinsetssourceconsumer.proto
+++ b/core/proto/android/view/imeinsetssourceconsumer.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
+import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
package android.view;
@@ -26,6 +27,7 @@
* Represents a {@link android.view.ImeInsetsSourceConsumer} object.
*/
message ImeInsetsSourceConsumerProto {
- optional .android.view.inputmethod.EditorInfoProto focused_editor = 1;
- optional bool is_requested_visible_awaiting_control = 2;
+ optional InsetsSourceConsumerProto insets_source_consumer = 1;
+ optional .android.view.inputmethod.EditorInfoProto focused_editor = 2;
+ optional bool is_requested_visible_awaiting_control = 3;
}
\ No newline at end of file
diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
index 7322139..f31d35b 100644
--- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
+++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
@@ -22,7 +22,6 @@
import "frameworks/base/core/proto/android/view/inputmethod/inputmethodmanager.proto";
import "frameworks/base/core/proto/android/view/viewrootimpl.proto";
import "frameworks/base/core/proto/android/view/insetscontroller.proto";
-import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto";
import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
import "frameworks/base/core/proto/android/view/imefocuscontroller.proto";
@@ -54,14 +53,19 @@
/* required: elapsed realtime in nanos since boot of when this entry was logged */
optional fixed64 elapsed_realtime_nanos = 1;
- optional ClientSideProto client_side_dump = 2;
+ optional ClientsProto clients = 2;
+
+ // this wrapper helps to simplify the dumping logic
+ message ClientsProto {
+ repeated ClientSideProto client = 1;
+ }
/* groups together the dump from ime related client side classes */
message ClientSideProto {
- optional InputMethodManagerProto input_method_manager = 1;
- optional ViewRootImplProto view_root_impl = 2;
- optional InsetsControllerProto insets_controller = 3;
- optional InsetsSourceConsumerProto insets_source_consumer = 4;
+ optional int32 display_id = 1;
+ optional InputMethodManagerProto input_method_manager = 2;
+ optional ViewRootImplProto view_root_impl = 3;
+ optional InsetsControllerProto insets_controller = 4;
optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5;
optional EditorInfoProto editor_info = 6;
optional ImeFocusControllerProto ime_focus_controller = 7;
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 86e7adf..9eaeed1 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1801,6 +1801,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "625447638": {
+ "message": "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"628276090": {
"message": "Delaying app transition for screen rotation animation to finish",
"level": "VERBOSE",
@@ -2113,12 +2119,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
},
- "1160771501": {
- "message": "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_RESIZE",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"1166381079": {
"message": "Execute app transition: %s, displayId: %d Callers=%s",
"level": "WARN",
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
new file mode 100644
index 0000000..9fc0c8e
--- /dev/null
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.graphics.Shader.TileMode;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Intermediate rendering step used to render drawing commands with a corresponding
+ * visual effect
+ *
+ * @hide
+ */
+public final class RenderEffect {
+
+ private static class RenderEffectHolder {
+ public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ RenderEffect.class.getClassLoader(), nativeGetFinalizer());
+ }
+
+ /**
+ * Create a {@link RenderEffect} instance that will offset the drawing content
+ * by the provided x and y offset.
+ * @param offsetX offset along the x axis in pixels
+ * @param offsetY offset along the y axis in pixels
+ */
+ @NonNull
+ public static RenderEffect createOffsetEffect(float offsetX, float offsetY) {
+ return new RenderEffect(nativeCreateOffsetEffect(offsetX, offsetY, 0));
+ }
+
+ /**
+ * Create a {@link RenderEffect} instance with the provided x and y offset
+ * @param offsetX offset along the x axis in pixels
+ * @param offsetY offset along the y axis in pixels
+ * @param input target RenderEffect used to render in the offset coordinates.
+ */
+ @NonNull
+ public static RenderEffect createOffsetEffect(
+ float offsetX,
+ float offsetY,
+ @NonNull RenderEffect input
+ ) {
+ return new RenderEffect(nativeCreateOffsetEffect(
+ offsetX,
+ offsetY,
+ input.getNativeInstance()
+ )
+ );
+ }
+
+ /**
+ * Create a {@link RenderEffect} that blurs the contents of the optional input RenderEffect
+ * with the specified radius along the x and y axis. If no input RenderEffect is provided
+ * then all drawing commands issued with a {@link android.graphics.RenderNode} that this
+ * RenderEffect is installed in will be blurred
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param inputEffect Input RenderEffect that provides the content to be blurred, can be null
+ * to indicate that the drawing commands on the RenderNode are to be
+ * blurred instead of the input RenderEffect
+ * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
+ */
+ @NonNull
+ public static RenderEffect createBlurEffect(
+ float radiusX,
+ float radiusY,
+ @NonNull RenderEffect inputEffect,
+ @NonNull TileMode edgeTreatment
+ ) {
+ long nativeInputEffect = inputEffect != null ? inputEffect.mNativeRenderEffect : 0;
+ return new RenderEffect(
+ nativeCreateBlurEffect(
+ radiusX,
+ radiusY,
+ nativeInputEffect,
+ edgeTreatment.nativeInt
+ )
+ );
+ }
+
+ /**
+ * Create a {@link RenderEffect} that blurs the contents of the
+ * {@link android.graphics.RenderNode} that this RenderEffect is installed on with the
+ * specified radius along hte x and y axis.
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
+ */
+ @NonNull
+ public static RenderEffect createBlurEffect(
+ float radiusX,
+ float radiusY,
+ @NonNull TileMode edgeTreatment
+ ) {
+ return new RenderEffect(
+ nativeCreateBlurEffect(
+ radiusX,
+ radiusY,
+ 0,
+ edgeTreatment.nativeInt
+ )
+ );
+ }
+
+ private final long mNativeRenderEffect;
+
+ /* only constructed from static factory methods */
+ private RenderEffect(long nativeRenderEffect) {
+ mNativeRenderEffect = nativeRenderEffect;
+ RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
+ this, mNativeRenderEffect);
+ }
+
+ /**
+ * Obtain the pointer to the underlying RenderEffect to be configured
+ * on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)}
+ */
+ /* package */ long getNativeInstance() {
+ return mNativeRenderEffect;
+ }
+
+ private static native long nativeCreateOffsetEffect(
+ float offsetX, float offsetY, long nativeInput);
+ private static native long nativeCreateBlurEffect(
+ float radiusX, float radiusY, long nativeInput, int edgeTreatment);
+ private static native long nativeGetFinalizer();
+}
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 8aacbc7..d812c1a 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -850,6 +850,23 @@
}
/**
+ * Configure the {@link android.graphics.RenderEffect} to apply to this RenderNode. This
+ * will apply a visual effect to the end result of the contents of this RenderNode before
+ * it is drawn into the destination. For example if
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+ * is provided, the contents will be drawn in a separate layer, then this layer will
+ * be blurred when this RenderNode is drawn into the destination.
+ * @param renderEffect to be applied to the RenderNode. Passing null clears all previously
+ * configured RenderEffects
+ *
+ * @hide
+ */
+ public void setRenderEffect(@Nullable RenderEffect renderEffect) {
+ nSetRenderEffect(mNativeRenderNode,
+ renderEffect != null ? renderEffect.getNativeInstance() : 0);
+ }
+
+ /**
* Returns the translucency level of this display list.
*
* @return A value between 0.0f and 1.0f
@@ -1655,6 +1672,9 @@
private static native boolean nSetAlpha(long renderNode, float alpha);
@CriticalNative
+ private static native void nSetRenderEffect(long renderNode, long renderEffect);
+
+ @CriticalNative
private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index cbae675..2a52ce9 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -26,8 +26,11 @@
import android.graphics.RectF;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.util.LongSparseArray;
import android.util.TypedValue;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
@@ -40,6 +43,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
@@ -56,6 +60,15 @@
private static final int STYLE_ITALIC = 1;
private static final int STYLE_NORMAL = 0;
+ private static final Object MAP_LOCK = new Object();
+ // We need to have mapping from native ptr to Font object for later accessing from TextShape
+ // result since Typeface doesn't have reference to Font object and it is not always created from
+ // Font object. Sometimes Typeface is created in native layer only and there might not be Font
+ // object in Java layer. So, if not found in this cache, create new Font object for API user.
+ @GuardedBy("MAP_LOCK")
+ private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
+ new LongSparseArray<>();
+
/**
* A builder class for creating new Font.
*/
@@ -501,6 +514,10 @@
mTtcIndex = ttcIndex;
mAxes = axes;
mLocaleList = localeList;
+
+ synchronized (MAP_LOCK) {
+ FONT_PTR_MAP.append(mNativePtr, new WeakReference<>(this));
+ }
}
/**
@@ -637,6 +654,63 @@
+ "}";
}
+ /**
+ * Lookup Font object from native pointer or create new one if not found.
+ * @hide
+ */
+ public static Font findOrCreateFontFromNativePtr(long ptr) {
+ // First, lookup from known mapps.
+ synchronized (MAP_LOCK) {
+ WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
+ if (fontRef != null) {
+ Font font = fontRef.get();
+ if (font != null) {
+ return font;
+ }
+ }
+
+ // If not found, create Font object from native object for Java API users.
+ ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
+ long packed = nGetFontInfo(ptr);
+ int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
+ boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
+ int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
+ int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
+ FontVariationAxis[] axes = new FontVariationAxis[axisCount];
+ char[] charBuffer = new char[4];
+ for (int i = 0; i < axisCount; ++i) {
+ long packedAxis = nGetAxisInfo(ptr, i);
+ float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+ charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
+ charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
+ charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
+ charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
+ axes[i] = new FontVariationAxis(new String(charBuffer), value);
+ }
+ Font.Builder builder = new Font.Builder(buffer)
+ .setWeight(weight)
+ .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
+ .setTtcIndex(ttcIndex)
+ .setFontVariationSettings(axes);
+
+ Font newFont = null;
+ try {
+ newFont = builder.build();
+ FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
+ } catch (IOException e) {
+ // This must not happen since the buffer was already created once.
+ Log.e("Font", "Failed to create font object from existing buffer.", e);
+ }
+ return newFont;
+ }
+ }
+
+ @CriticalNative
+ private static native long nGetFontInfo(long ptr);
+
+ @CriticalNative
+ private static native long nGetAxisInfo(long ptr, int i);
+
@FastNative
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
new file mode 100644
index 0000000..5655e7f
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import android.annotation.NonNull;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This is a helper class for showing native allocated buffer in Java API.
+ *
+ * @hide
+ */
+public class NativeFontBufferHelper {
+ private NativeFontBufferHelper() {}
+
+ private static final NativeAllocationRegistry REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
+
+ /**
+ * Wrap native buffer with ByteBuffer with adding reference to it.
+ */
+ public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
+ long refPtr = nRefFontBuffer(fontPtr);
+ ByteBuffer buffer = nWrapByteBuffer(refPtr);
+
+ // Releasing native object so that decreasing shared pointer ref count when the byte buffer
+ // is GCed.
+ REGISTRY.registerNativeAllocation(buffer, refPtr);
+
+ return buffer;
+ }
+
+ @CriticalNative
+ private static native long nRefFontBuffer(long fontPtr);
+
+ @FastNative
+ private static native ByteBuffer nWrapByteBuffer(long refPtr);
+
+ @CriticalNative
+ private static native long nGetReleaseFunc();
+}
diff --git a/graphics/java/android/graphics/text/GlyphStyle.java b/graphics/java/android/graphics/text/GlyphStyle.java
new file mode 100644
index 0000000..cc8c4d2
--- /dev/null
+++ b/graphics/java/android/graphics/text/GlyphStyle.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.ColorInt;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+
+import java.util.Objects;
+
+/**
+ * Represents subset of Paint parameters such as font size, scaleX that is used to draw a glyph.
+ *
+ * Glyph is a most primitive unit of text drawing.
+ *
+ */
+public class GlyphStyle {
+ private @ColorInt int mColor;
+ private float mFontSize;
+ private float mScaleX;
+ private float mSkewX;
+ private int mFlags;
+
+ /**
+ * @param color a color.
+ * @param fontSize a font size in pixels.
+ * @param scaleX a horizontal scale factor.
+ * @param skewX a horizontal skew factor
+ * @param flags paint flags
+ *
+ * @see Paint#getFlags()
+ * @see Paint#setFlags(int)
+ */
+ public GlyphStyle(
+ @ColorInt int color,
+ @FloatRange(from = 0) float fontSize,
+ @FloatRange(from = 0) float scaleX,
+ @FloatRange(from = 0) float skewX,
+ int flags) {
+ mColor = color;
+ mFontSize = fontSize;
+ mScaleX = scaleX;
+ mSkewX = skewX;
+ mFlags = flags;
+ }
+
+ /**
+ * Create glyph style from Paint
+ *
+ * @param paint a paint
+ */
+ public GlyphStyle(@NonNull Paint paint) {
+ setFromPaint(paint);
+ }
+
+ /**
+ * Gets the color.
+ *
+ * @return a color
+ * @see Paint#getColor()
+ * @see Paint#setColor(int)
+ */
+ public @ColorInt int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Sets the color.
+ *
+ * @param color a color
+ * @see Paint#getColor()
+ * @see Paint#setColor(int)
+ */
+ public void setColor(@ColorInt int color) {
+ mColor = color;
+ }
+
+ /**
+ * Gets the font size in pixels.
+ *
+ * @return font size
+ * @see Paint#getTextSize()
+ * @see Paint#setTextSize(float)
+ */
+ public @FloatRange(from = 0) float getFontSize() {
+ return mFontSize;
+ }
+
+ /**
+ * Sets the font size in pixels.
+ *
+ * @param fontSize font size in pixel
+ * @see Paint#getTextSize()
+ * @see Paint#setTextSize(float)
+ */
+ public void setFontSize(@FloatRange(from = 0) float fontSize) {
+ mFontSize = fontSize;
+ }
+
+ /**
+ * Return the horizontal scale factor
+ *
+ * @return a horizontal scale factor
+ * @see Paint#getTextScaleX()
+ * @see Paint#setTextScaleX(float)
+ */
+ public @FloatRange(from = 0) float getScaleX() {
+ return mScaleX;
+ }
+
+ /**
+ * Set the horizontal scale factor
+ *
+ * @param scaleX a horizontal scale factor
+ * @see Paint#getTextScaleX()
+ * @see Paint#setTextScaleX(float)
+ */
+ public void setScaleX(@FloatRange(from = 0) float scaleX) {
+ mScaleX = scaleX;
+ }
+
+ /**
+ * Return the horizontal skew factor
+ *
+ * @return a horizontal skew factor
+ * @see Paint#getTextSkewX()
+ * @see Paint#setTextSkewX(float)
+ */
+ public @FloatRange(from = 0) float getSkewX() {
+ return mSkewX;
+ }
+
+ /**
+ * Set the horizontal skew factor
+ *
+ * @param skewX a horizontal skew factor
+ * @see Paint#getTextSkewX()
+ * @see Paint#setTextSkewX(float)
+ */
+ public void setSkewX(@FloatRange(from = 0) float skewX) {
+ mSkewX = skewX;
+ }
+
+ /**
+ * Returns the Paint flags.
+ *
+ * @return a paint flags
+ * @see Paint#getFlags()
+ * @see Paint#setFlags(int)
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Set the Paint flags.
+ *
+ * @param flags a paint flags
+ * @see Paint#getFlags()
+ * @see Paint#setFlags(int)
+ */
+ public void setFlags(int flags) {
+ mFlags = flags;
+ }
+
+ /**
+ * Applies glyph style to the paint object.
+ *
+ * @param paint a paint object
+ */
+ public void applyToPaint(@NonNull Paint paint) {
+ paint.setColor(mColor);
+ paint.setTextSize(mFontSize);
+ paint.setTextScaleX(mScaleX);
+ paint.setTextSkewX(mSkewX);
+ paint.setFlags(mFlags);
+ }
+
+ /**
+ * Copy parameters from a Paint object.
+ *
+ * @param paint a paint object
+ */
+ public void setFromPaint(@NonNull Paint paint) {
+ mColor = paint.getColor();
+ mFontSize = paint.getTextSize();
+ mScaleX = paint.getTextScaleX();
+ mSkewX = paint.getTextSkewX();
+ mFlags = paint.getFlags();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof GlyphStyle)) return false;
+ GlyphStyle that = (GlyphStyle) o;
+ return that.mColor == mColor
+ && Float.compare(that.mFontSize, mFontSize) == 0
+ && Float.compare(that.mScaleX, mScaleX) == 0
+ && Float.compare(that.mSkewX, mSkewX) == 0
+ && mFlags == that.mFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mColor, mFontSize, mScaleX, mSkewX, mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "GlyphStyle{"
+ + "mColor=" + mColor
+ + ", mFontSize=" + mFontSize
+ + ", mScaleX=" + mScaleX
+ + ", mSkewX=" + mSkewX
+ + ", mFlags=" + mFlags
+ + '}';
+ }
+}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
new file mode 100644
index 0000000..7364d54
--- /dev/null
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.fonts.Font;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * Text shaping result object for single style text.
+ *
+ * You can get text shaping result by
+ * {@link TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
+ * {@link TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}.
+ *
+ * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ */
+public final class PositionedGlyphs {
+ private static final NativeAllocationRegistry REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ Typeface.class.getClassLoader(), nReleaseFunc());
+
+ private final long mLayoutPtr;
+ private final float mXOffset;
+ private final float mYOffset;
+ private final GlyphStyle mGlyphStyle;
+ private final ArrayList<Font> mFonts;
+
+ /**
+ * Returns the total amount of advance consumed by this positioned glyphs.
+ *
+ * The advance is an amount of width consumed by the glyph. The total amount of advance is
+ * a total amount of advance consumed by this series of glyphs. In other words, if another
+ * glyph is placed next to this series of glyphs, it's X offset should be shifted this amount
+ * of width.
+ *
+ * @return total amount of advance
+ */
+ public float getTotalAdvance() {
+ return nGetTotalAdvance(mLayoutPtr);
+ }
+
+ /**
+ * Effective ascent value of this positioned glyphs.
+ *
+ * If two or more font files are used in this series of glyphs, the effective ascent will be
+ * the minimum ascent value across the all font files.
+ *
+ * @return effective ascent value
+ */
+ public float getAscent() {
+ return nGetAscent(mLayoutPtr);
+ }
+
+ /**
+ * Effective descent value of this positioned glyphs.
+ *
+ * If two or more font files are used in this series of glyphs, the effective descent will be
+ * the maximum descent value across the all font files.
+ *
+ * @return effective descent value
+ */
+ public float getDescent() {
+ return nGetDescent(mLayoutPtr);
+ }
+
+ /**
+ * Returns the glyph style used for drawing the glyph at the given index.
+ *
+ * @return A glyph style
+ */
+ @NonNull
+ public GlyphStyle getStyle() {
+ return mGlyphStyle;
+ }
+
+ /**
+ * Returns the amount of X offset added to glyph position.
+ *
+ * @return The X offset added to glyph position.
+ */
+ public float getOriginX() {
+ return mXOffset;
+ }
+
+ /**
+ * Returns the amount of Y offset added to glyph position.
+ *
+ * @return The Y offset added to glyph position.
+ */
+ public float getOriginY() {
+ return mYOffset;
+ }
+
+ /**
+ * Returns the number of glyphs stored.
+ *
+ * @return the number of glyphs
+ */
+ @IntRange(from = 0)
+ public int glyphCount() {
+ return nGetGlyphCount(mLayoutPtr);
+ }
+
+ /**
+ * Returns the font object used for drawing the glyph at the given index.
+ *
+ * @param index the glyph index
+ * @return the font object used for drawing the glyph at the given index
+ */
+ @NonNull
+ public Font getFont(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return mFonts.get(index);
+ }
+
+ /**
+ * Returns the glyph ID used for drawing the glyph at the given index.
+ *
+ * @param index the glyph index
+ * @return A font object
+ */
+ @IntRange(from = 0)
+ public int getGlyphId(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetGlyphId(mLayoutPtr, index);
+ }
+
+ /**
+ * Returns the x coordinate of the glyph position at the given index.
+ *
+ * @param index the glyph index
+ * @return A X offset in pixels
+ */
+ public float getPositionX(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetX(mLayoutPtr, index) + mXOffset;
+ }
+
+ /**
+ * Returns the y coordinate of the glyph position at the given index.
+ *
+ * @param index the glyph index
+ * @return A Y offset in pixels.
+ */
+ public float getPositionY(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetY(mLayoutPtr, index) + mYOffset;
+ }
+
+ /**
+ * Create single style layout from native result.
+ *
+ * @hide
+ *
+ * @param layoutPtr the address of native layout object.
+ * @param paint a paint object
+ */
+ public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) {
+ mLayoutPtr = layoutPtr;
+ mGlyphStyle = new GlyphStyle(paint);
+ int glyphCount = nGetGlyphCount(layoutPtr);
+ mFonts = new ArrayList<>(glyphCount);
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+
+ long prevPtr = 0;
+ Font prevFont = null;
+ for (int i = 0; i < glyphCount; ++i) {
+ long ptr = nGetFont(layoutPtr, i);
+ if (prevPtr != ptr) {
+ prevPtr = ptr;
+ prevFont = Font.findOrCreateFontFromNativePtr(ptr);
+ }
+ mFonts.add(prevFont);
+ }
+
+ REGISTRY.registerNativeAllocation(this, layoutPtr);
+ }
+
+ @CriticalNative
+ private static native int nGetGlyphCount(long minikinLayout);
+ @CriticalNative
+ private static native float nGetTotalAdvance(long minikinLayout);
+ @CriticalNative
+ private static native float nGetAscent(long minikinLayout);
+ @CriticalNative
+ private static native float nGetDescent(long minikinLayout);
+ @CriticalNative
+ private static native int nGetGlyphId(long minikinLayout, int i);
+ @CriticalNative
+ private static native float nGetX(long minikinLayout, int i);
+ @CriticalNative
+ private static native float nGetY(long minikinLayout, int i);
+ @CriticalNative
+ private static native long nGetFont(long minikinLayout, int i);
+ @CriticalNative
+ private static native long nReleaseFunc();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PositionedGlyphs)) return false;
+ PositionedGlyphs that = (PositionedGlyphs) o;
+
+ if (!mGlyphStyle.equals(that.mGlyphStyle)) return false;
+ if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false;
+ if (glyphCount() != that.glyphCount()) return false;
+
+ for (int i = 0; i < glyphCount(); ++i) {
+ if (getGlyphId(i) != that.getGlyphId(i)) return false;
+ if (getPositionX(i) != that.getPositionX(i)) return false;
+ if (getPositionY(i) != that.getPositionY(i)) return false;
+ // Intentionally using reference equality since font equality is heavy due to buffer
+ // compare.
+ if (getFont(i) != that.getFont(i)) return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = Objects.hash(mXOffset, mYOffset, mGlyphStyle);
+ for (int i = 0; i < glyphCount(); ++i) {
+ hashCode = Objects.hash(hashCode,
+ getGlyphId(i), getPositionX(i), getPositionY(i), getFont(i));
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("[");
+ for (int i = 0; i < glyphCount(); ++i) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append("[ ID = " + getGlyphId(i) + ","
+ + " pos = (" + getPositionX(i) + "," + getPositionY(i) + ")"
+ + " font = " + getFont(i) + " ]");
+ }
+ sb.append("]");
+ return "PositionedGlyphs{"
+ + "glyphs = " + sb.toString()
+ + ", mXOffset=" + mXOffset
+ + ", mYOffset=" + mYOffset
+ + ", mGlyphStyle=" + mGlyphStyle
+ + '}';
+ }
+}
diff --git a/graphics/java/android/graphics/text/TextShaper.java b/graphics/java/android/graphics/text/TextShaper.java
new file mode 100644
index 0000000..f40ed8f
--- /dev/null
+++ b/graphics/java/android/graphics/text/TextShaper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.text.TextDirectionHeuristic;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.FastNative;
+
+/**
+ * Provides conversion from a text into glyph array.
+ *
+ * Text shaping is a preprocess for drawing text into canvas with glyphs. The glyph is a most
+ * primitive unit of the text drawing, consist of glyph identifier in the font file and its position
+ * and style. You can draw the shape result to Canvas by calling Canvas#drawGlyphs.
+
+ *
+ * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see android.text.StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic,
+ * TextPaint)
+ */
+public class TextShaper {
+ private TextShaper() {} // Do not instantiate
+
+ /**
+ * Shape non-styled text.
+ *
+ * This function shapes the text of the given range under the context of given context range.
+ * Some script, e.g. Arabic or Devanagari, changes letter shape based on its location or
+ * surrounding characters.
+ *
+ * @param text a text buffer to be shaped
+ * @param start a start index of shaping target in the buffer.
+ * @param count a length of shaping target in the buffer.
+ * @param contextStart a start index of context used for shaping in the buffer.
+ * @param contextCount a length of context used for shaping in the buffer.
+ * @param xOffset an additional amount of x offset of the result glyphs.
+ * @param yOffset an additional amount of y offset of the result glyphs.
+ * @param isRtl true if this text is shaped for RTL direction, false otherwise.
+ * @param paint a paint used for shaping text.
+ * @return a shape result.
+ */
+ @NonNull
+ public static PositionedGlyphs shapeTextRun(
+ @NonNull char[] text, int start, int count, int contextStart, int contextCount,
+ float xOffset, float yOffset, boolean isRtl, @NonNull Paint paint) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ return new PositionedGlyphs(
+ nativeShapeTextRun(text, start, count, contextStart, contextCount, isRtl,
+ paint.getNativeInstance()),
+ paint, xOffset, yOffset);
+ }
+
+ /**
+ * Shape non-styled text.
+ *
+ * This function shapes the text of the given range under the context of given context range.
+ * Some script, e.g. Arabic or Devanagari, changes letter shape based on its location or
+ * surrounding characters.
+ *
+ * @param text a text buffer to be shaped. Any styled spans stored in this text are ignored.
+ * @param start a start index of shaping target in the buffer.
+ * @param count a length of shaping target in the buffer.
+ * @param contextStart a start index of context used for shaping in the buffer.
+ * @param contextCount a length of context used for shaping in the buffer.
+ * @param xOffset an additional amount of x offset of the result glyphs.
+ * @param yOffset an additional amount of y offset of the result glyphs.
+ * @param isRtl true if this text is shaped for RTL direction, false otherwise.
+ * @param paint a paint used for shaping text.
+ * @return a shape result
+ */
+ @NonNull
+ public static PositionedGlyphs shapeTextRun(
+ @NonNull CharSequence text, int start, int count, int contextStart, int contextCount,
+ float xOffset, float yOffset, boolean isRtl, @NonNull Paint paint) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ if (text instanceof String) {
+ return new PositionedGlyphs(
+ nativeShapeTextRun(
+ (String) text, start, count, contextStart, contextCount, isRtl,
+ paint.getNativeInstance()),
+ paint, xOffset, yOffset);
+ } else {
+ char[] buf = new char[contextCount];
+ TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
+ return new PositionedGlyphs(
+ nativeShapeTextRun(
+ buf, start - contextStart, count,
+ 0, contextCount, isRtl, paint.getNativeInstance()),
+ paint, xOffset, yOffset);
+ }
+ }
+
+ @FastNative
+ private static native long nativeShapeTextRun(
+ char[] text, int start, int count, int contextStart, int contextCount,
+ boolean isRtl, long nativePaint);
+
+ @FastNative
+ private static native long nativeShapeTextRun(
+ String text, int start, int count, int contextStart, int contextCount,
+ boolean isRtl, long nativePaint);
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 9954618..6bc838f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -239,9 +239,14 @@
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
mDisplayAreaMap.forEach(
- (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
- durationMs));
+ (key, leash) -> {
+ animateWindows(leash, fromBounds, toBounds, direction,
+ durationMs);
+ wct.setBounds(key.token, toBounds);
+ });
+ applyTransaction(wct);
}
private void resetWindowsOffset() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 7c0c738..b6b518d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -184,9 +184,8 @@
mDisplaySize.x, mTutorialAreaHeight, 0, 0,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.TOP | Gravity.LEFT;
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setFitInsetsTypes(0 /* types */);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 90d2537..975265e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -327,6 +327,7 @@
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
"jni/Shader.cpp",
+ "jni/RenderEffect.cpp",
"jni/Typeface.cpp",
"jni/Utils.cpp",
"jni/YuvToJpegEncoder.cpp",
@@ -334,6 +335,7 @@
"jni/fonts/FontFamily.cpp",
"jni/text/LineBreaker.cpp",
"jni/text/MeasuredText.cpp",
+ "jni/text/TextShaper.cpp",
],
header_libs: [ "android_graphics_jni_headers" ],
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ff9cf45..8fba9cf 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -49,6 +49,12 @@
return true;
}
+bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) {
+ if(mImageFilter.get() == imageFilter) return false;
+ mImageFilter = sk_ref_sp(imageFilter);
+ return true;
+}
+
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
@@ -63,6 +69,7 @@
setAlpha(other.alpha());
setXferMode(other.xferMode());
setColorFilter(other.getColorFilter());
+ setImageFilter(other.getImageFilter());
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ef4cd1f..aeb60e6 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -27,6 +27,7 @@
#include "utils/PaintUtils.h"
#include <SkBlendMode.h>
+#include <SkImageFilter.h>
#include <SkCamera.h>
#include <SkColor.h>
#include <SkMatrix.h>
@@ -93,6 +94,10 @@
SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+ bool setImageFilter(SkImageFilter* imageFilter);
+
+ SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
bool setFromPaint(const SkPaint* paint);
@@ -118,6 +123,7 @@
uint8_t mAlpha;
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
+ sk_sp<SkImageFilter> mImageFilter;
};
/*
@@ -541,6 +547,7 @@
bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
(mComputedFields.mNeedLayerForFunctors ||
+ mLayerProperties.mImageFilter != nullptr ||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
mPrimitiveFields.mHasOverlappingRendering));
}
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 4bbf121..dca10e2 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -47,6 +47,7 @@
extern int register_android_graphics_NinePatch(JNIEnv*);
extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
namespace android {
@@ -108,6 +109,7 @@
{"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
+ {"android.graphics.RenderEffect", REG_JNI(register_android_graphics_RenderEffect)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
{"android.graphics.animation.NativeInterpolatorFactory",
REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 12e2e81..e1f5abd7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -43,6 +43,7 @@
extern int register_android_graphics_NinePatch(JNIEnv*);
extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
extern int register_android_graphics_YuvImage(JNIEnv* env);
@@ -73,6 +74,7 @@
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
+extern int register_android_graphics_text_TextShaper(JNIEnv *env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
@@ -123,6 +125,7 @@
REG_JNI(register_android_graphics_Picture),
REG_JNI(register_android_graphics_Region),
REG_JNI(register_android_graphics_Shader),
+ REG_JNI(register_android_graphics_RenderEffect),
REG_JNI(register_android_graphics_TextureLayer),
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_YuvImage),
@@ -137,6 +140,7 @@
REG_JNI(register_android_graphics_pdf_PdfRenderer),
REG_JNI(register_android_graphics_text_MeasuredText),
REG_JNI(register_android_graphics_text_LineBreaker),
+ REG_JNI(register_android_graphics_text_TextShaper),
REG_JNI(register_android_util_PathParser),
REG_JNI(register_android_view_RenderNode),
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 6cde9c5..a15803a 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -72,7 +72,7 @@
size_t start = 0;
size_t nGlyphs = layout.nGlyphs();
for (size_t i = 0; i < nGlyphs; i++) {
- const minikin::MinikinFont* nextFont = layout.getFont(i);
+ const minikin::MinikinFont* nextFont = layout.getFont(i)->typeface().get();
if (i > 0 && nextFont != curFont) {
SkFont* skfont = &paint->getSkFont();
MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 0bb689c..4e2016a 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -157,7 +157,6 @@
private:
using SkPaint::setShader;
- using SkPaint::setImageFilter;
SkFont mFont;
sk_sp<SkDrawLooper> mLooper;
diff --git a/libs/hwui/jni/FontUtils.h b/libs/hwui/jni/FontUtils.h
index b36b4e6..f93a0da 100644
--- a/libs/hwui/jni/FontUtils.h
+++ b/libs/hwui/jni/FontUtils.h
@@ -38,6 +38,14 @@
minikin::Font font;
};
+// We assume FontWrapper's address is the same as underlying Font's address.
+// This assumption is used for looking up Java font object from native address.
+// The Font object can be created without Java's Font object but all Java's Font objects point to
+// the native FontWrapper. So when looking up Java object from minikin::Layout which gives us Font
+// address, we lookup Font Java object from Font address with assumption that it is the same as
+// FontWrapper address.
+static_assert(offsetof(FontWrapper, font) == 0);
+
// Utility wrapper for java.util.List
class ListHelper {
public:
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
new file mode 100644
index 0000000..0ebd0ca
--- /dev/null
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Bitmap.h"
+#include "GraphicsJNI.h"
+#include "SkImageFilter.h"
+#include "SkImageFilters.h"
+#include "graphics_jni_helpers.h"
+#include "utils/Blur.h"
+#include <utils/Log.h>
+
+using namespace android::uirenderer;
+
+static jlong createOffsetEffect(
+ JNIEnv* env,
+ jobject,
+ jfloat offsetX,
+ jfloat offsetY,
+ jlong inputFilterHandle
+) {
+ auto* inputFilter = reinterpret_cast<const SkImageFilter*>(inputFilterHandle);
+ sk_sp<SkImageFilter> offset = SkImageFilters::Offset(offsetX, offsetY, sk_ref_sp(inputFilter));
+ return reinterpret_cast<jlong>(offset.release());
+}
+
+static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX,
+ jfloat radiusY, jlong inputFilterHandle, jint edgeTreatment) {
+ auto* inputImageFilter = reinterpret_cast<SkImageFilter*>(inputFilterHandle);
+ sk_sp<SkImageFilter> blurFilter =
+ SkImageFilters::Blur(
+ Blur::convertRadiusToSigma(radiusX),
+ Blur::convertRadiusToSigma(radiusY),
+ static_cast<SkTileMode>(edgeTreatment),
+ sk_ref_sp(inputImageFilter),
+ nullptr);
+ return reinterpret_cast<jlong>(blurFilter.release());
+}
+
+static void RenderEffect_safeUnref(SkImageFilter* filter) {
+ SkSafeUnref(filter);
+}
+
+static jlong getRenderEffectFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RenderEffect_safeUnref));
+}
+
+static const JNINativeMethod gRenderEffectMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
+ {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
+ {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect}
+};
+
+int register_android_graphics_RenderEffect(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
+ gRenderEffectMethods, NELEM(gRenderEffectMethods));
+ return 0;
+}
\ No newline at end of file
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 85c802b..4b4aa92 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -215,6 +215,12 @@
return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
}
+static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+ jlong renderEffectPtr) {
+ SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
+ return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
+}
+
static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
bool hasOverlappingRendering) {
return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
@@ -690,6 +696,7 @@
{ "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
{ "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
+ { "nSetRenderEffect", "(JJ)V", (void*) android_view_RenderNode_setRenderEffect },
{ "nSetHasOverlappingRendering", "(JZ)Z",
(void*) android_view_RenderNode_setHasOverlappingRendering },
{ "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index bcdb4f5..0eb4095 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -198,6 +198,59 @@
return spacing;
}
+// Critical Native
+static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+
+ uint64_t result = font->font.style().weight();
+ result |= font->font.style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
+ result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
+ result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
+ return result;
+}
+
+// Critical Native
+static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
+ uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+ return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct FontBufferWrapper {
+ FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
+ // MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
+ std::shared_ptr<minikin::MinikinFont> minikinFont;
+};
+
+static void unrefBuffer(jlong nativePtr) {
+ FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
+ delete wrapper;
+}
+
+// Critical Native
+static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ return reinterpret_cast<jlong>(new FontBufferWrapper(font->font.typeface()));
+}
+
+// Fast Native
+static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
+ FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
+ return env->NewDirectByteBuffer(
+ const_cast<void*>(wrapper->minikinFont->GetFontData()),
+ wrapper->minikinFont->GetFontSize());
+}
+
+// Critical Native
+static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
+ return reinterpret_cast<jlong>(unrefBuffer);
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontBuilderMethods[] = {
@@ -211,13 +264,23 @@
static const JNINativeMethod gFontMethods[] = {
{ "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
{ "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
+ { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo },
+ { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
+};
+
+static const JNINativeMethod gFontBufferHelperMethods[] = {
+ { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
+ { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
+ { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
};
int register_android_graphics_fonts_Font(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
NELEM(gFontBuilderMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
- NELEM(gFontMethods));
+ NELEM(gFontMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
+ gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods));
}
}
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
new file mode 100644
index 0000000..9d9e91f
--- /dev/null
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TextShaper"
+
+#include "graphics_jni_helpers.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <set>
+#include <algorithm>
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/MinikinPaint.h>
+#include <minikin/MinikinFont.h>
+
+namespace android {
+
+struct LayoutWrapper {
+ LayoutWrapper(minikin::Layout&& layout, float ascent, float descent)
+ : layout(std::move(layout)), ascent(ascent), descent(descent) {}
+ minikin::Layout layout;
+ float ascent;
+ float descent;
+};
+
+static void releaseLayout(jlong ptr) {
+ delete reinterpret_cast<LayoutWrapper*>(ptr);
+}
+
+static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int count,
+ int contextStart, int contextCount, minikin::Bidi bidiFlags,
+ const Paint& paint, const Typeface* typeface) {
+
+ minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(&paint, typeface);
+
+ minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface,
+ text, textSize, start, count, contextStart, contextCount, nullptr);
+
+ std::set<const minikin::Font*> seenFonts;
+ float overallAscent = 0;
+ float overallDescent = 0;
+ for (int i = 0; i < layout.nGlyphs(); ++i) {
+ const minikin::Font* font = layout.getFont(i);
+ if (seenFonts.find(font) != seenFonts.end()) continue;
+ minikin::MinikinExtent extent = {};
+ font->typeface()->GetFontExtent(&extent, minikinPaint, layout.getFakery(i));
+ overallAscent = std::min(overallAscent, extent.ascent);
+ overallDescent = std::max(overallDescent, extent.descent);
+ }
+
+ std::unique_ptr<LayoutWrapper> ptr = std::make_unique<LayoutWrapper>(
+ std::move(layout), overallAscent, overallDescent
+ );
+
+ return reinterpret_cast<jlong>(ptr.release());
+}
+
+static jlong TextShaper_shapeTextRunChars(JNIEnv *env, jobject, jcharArray charArray,
+ jint start, jint count, jint contextStart, jint contextCount, jboolean isRtl,
+ jlong paintPtr) {
+ ScopedCharArrayRO text(env, charArray);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ return shapeTextRun(
+ text.get(), text.size(),
+ start, count,
+ contextStart, contextCount,
+ bidiFlags,
+ *paint, typeface);
+
+}
+
+static jlong TextShaper_shapeTextRunString(JNIEnv *env, jobject, jstring string,
+ jint start, jint count, jint contextStart, jint contextCount, jboolean isRtl,
+ jlong paintPtr) {
+ ScopedStringChars text(env, string);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ return shapeTextRun(
+ text.get(), text.size(),
+ start, count,
+ contextStart, contextCount,
+ bidiFlags,
+ *paint, typeface);
+}
+
+// CriticalNative
+static jint TextShaper_Result_getGlyphCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.nGlyphs();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getTotalAdvance(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getAdvance();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->ascent;
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->descent;
+}
+
+// CriticalNative
+static jint TextShaper_Result_getGlyphId(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getGlyphId(i);
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getX(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getX(i);
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getY(i);
+}
+
+// CriticalNative
+static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return reinterpret_cast<jlong>(layout->layout.getFont(i));
+}
+
+// CriticalNative
+static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
+ return reinterpret_cast<jlong>(releaseLayout);
+}
+
+static const JNINativeMethod gMethods[] = {
+ // Fast Natives
+ {"nativeShapeTextRun", "("
+ "[C" // text
+ "I" // start
+ "I" // count
+ "I" // contextStart
+ "I" // contextCount
+ "Z" // isRtl
+ "J)" // paint
+ "J", // LayoutPtr
+ (void*) TextShaper_shapeTextRunChars},
+
+ {"nativeShapeTextRun", "("
+ "Ljava/lang/String;" // text
+ "I" // start
+ "I" // count
+ "I" // contextStart
+ "I" // contextCount
+ "Z" // isRtl
+ "J)" // paint
+ "J", // LayoutPtr
+ (void*) TextShaper_shapeTextRunString},
+
+};
+
+static const JNINativeMethod gResultMethods[] = {
+ { "nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount },
+ { "nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance },
+ { "nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent },
+ { "nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent },
+ { "nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId },
+ { "nGetX", "(JI)F", (void*)TextShaper_Result_getX },
+ { "nGetY", "(JI)F", (void*)TextShaper_Result_getY },
+ { "nGetFont", "(JI)J", (void*)TextShaper_Result_getFont },
+ { "nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc },
+};
+
+int register_android_graphics_text_TextShaper(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/text/TextShaper", gMethods,
+ NELEM(gMethods))
+ + RegisterMethodsOrDie(env, "android/graphics/text/PositionedGlyphs",
+ gResultMethods, NELEM(gResultMethods));
+}
+
+}
+
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 00ceb2d..1473b3e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -172,10 +172,12 @@
SkPaint* paint) {
paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
- properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
+ properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
+ properties.getImageFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
+ paint->setImageFilter(sk_ref_sp(properties.getImageFilter()));
return true;
}
return false;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index d35bc41..d02b496 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -372,7 +372,7 @@
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
startLocalPlayer();
}
- } else if (mAllowRemote && (mRemotePlayer != null)) {
+ } else if (mAllowRemote && (mRemotePlayer != null) && (mUri != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
final boolean looping;
final float volume;
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 8deb0c4..9deeb8f 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -34,6 +34,7 @@
import android.content.pm.UserInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.StaleDataException;
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
@@ -492,7 +493,12 @@
public Uri getRingtoneUri(int position) {
// use cursor directly instead of requerying it, which could easily
// cause position to shuffle.
- if (mCursor == null || !mCursor.moveToPosition(position)) {
+ try {
+ if (mCursor == null || !mCursor.moveToPosition(position)) {
+ return null;
+ }
+ } catch (StaleDataException | IllegalStateException e) {
+ Log.e(TAG, "Unexpected Exception has been catched.", e);
return null;
}
@@ -1130,11 +1136,13 @@
// Try finding the scanned ringtone
final String filename = getDefaultRingtoneFilename(type);
+ final String whichAudio = getQueryStringForType(type);
+ final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
try (Cursor cursor = context.getContentResolver().query(baseUri,
new String[] { MediaColumns._ID },
- MediaColumns.DISPLAY_NAME + "=?",
- new String[] { filename }, null)) {
+ where,
+ new String[] { filename, "1" }, null)) {
if (cursor.moveToFirst()) {
final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
@@ -1162,4 +1170,13 @@
default: throw new IllegalArgumentException();
}
}
+
+ private static String getQueryStringForType(int type) {
+ switch (type) {
+ case TYPE_RINGTONE: return MediaStore.Audio.AudioColumns.IS_RINGTONE;
+ case TYPE_NOTIFICATION: return MediaStore.Audio.AudioColumns.IS_NOTIFICATION;
+ case TYPE_ALARM: return MediaStore.Audio.AudioColumns.IS_ALARM;
+ default: throw new IllegalArgumentException();
+ }
+ }
}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 24fbe00..c5197ae 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -6138,6 +6138,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
+ field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000
field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000
field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000
@@ -16491,6 +16492,23 @@
package android.graphics.text {
+ public class GlyphStyle {
+ ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
+ ctor public GlyphStyle(@NonNull android.graphics.Paint);
+ method public void applyToPaint(@NonNull android.graphics.Paint);
+ method @ColorInt public int getColor();
+ method public int getFlags();
+ method @FloatRange(from=0) public float getFontSize();
+ method @FloatRange(from=0) public float getScaleX();
+ method @FloatRange(from=0) public float getSkewX();
+ method public void setColor(@ColorInt int);
+ method public void setFlags(int);
+ method public void setFontSize(@FloatRange(from=0) float);
+ method public void setFromPaint(@NonNull android.graphics.Paint);
+ method public void setScaleX(@FloatRange(from=0) float);
+ method public void setSkewX(@FloatRange(from=0) float);
+ }
+
public class LineBreaker {
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16551,6 +16569,25 @@
method @NonNull public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
}
+ public final class PositionedGlyphs {
+ method public float getAscent();
+ method public float getDescent();
+ method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
+ method public float getOriginX();
+ method public float getOriginY();
+ method public float getPositionX(@IntRange(from=0) int);
+ method public float getPositionY(@IntRange(from=0) int);
+ method @NonNull public android.graphics.text.GlyphStyle getStyle();
+ method public float getTotalAdvance();
+ method @IntRange(from=0) public int glyphCount();
+ }
+
+ public class TextShaper {
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ }
+
}
package android.hardware {
@@ -44936,6 +44973,7 @@
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -45130,6 +45168,10 @@
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+ field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+ field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+ field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
@@ -48131,6 +48173,10 @@
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
+ public class StyledTextShaper {
+ method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
+ }
+
public interface TextDirectionHeuristic {
method public boolean isRtl(char[], int, int);
method public boolean isRtl(CharSequence, int, int);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9788b30..0810979 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -437,6 +437,7 @@
Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED,
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
+ Settings.Global.SHOW_PEOPLE_SPACE,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index 92d0858..ac8b7b5 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -75,18 +75,6 @@
android:textSize="12sp"
android:fontFamily="roboto-regular"
android:visibility="gone"/>
- <ProgressBar
- android:id="@+id/volume_indeterminate_progress"
- style="@*android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
- android:layout_marginBottom="1dp"
- android:layout_alignParentBottom="true"
- android:indeterminate="true"
- android:indeterminateOnly="true"
- android:visibility="gone"/>
<SeekBar
android:id="@+id/volume_seekbar"
android:layout_width="match_parent"
@@ -94,6 +82,17 @@
android:layout_alignParentBottom="true"/>
</RelativeLayout>
+ <ProgressBar
+ android:id="@+id/volume_indeterminate_progress"
+ style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="258dp"
+ android:layout_height="18dp"
+ android:layout_marginStart="68dp"
+ android:layout_marginTop="40dp"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
+
<View
android:layout_width="1dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2e56df3..8777d50 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1380,4 +1380,5 @@
<dimen name="media_output_dialog_header_back_icon_size">36dp</dimen>
<dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
+ <dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
new file mode 100644
index 0000000..dccb24d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.systemui.emergency;
+
+/**
+ * Constants for the Emergency gesture.
+ *
+ * TODO (b/169175022) Update classname and docs when feature name is locked
+ */
+public final class EmergencyGesture {
+
+ /**
+ * Launches the emergency flow.
+ *
+ * <p>The emergency flow is triggered by the Emergency gesture. By default the flow will call
+ * local emergency services, though OEMs can customize the flow.
+ *
+ * <p>This action can only be triggered by System UI through the emergency gesture.
+ *
+ * <p>TODO (b/169175022) Update action name and docs when feature name is locked
+ */
+ public static final String ACTION_LAUNCH_EMERGENCY =
+ "com.android.systemui.action.LAUNCH_EMERGENCY";
+
+ private EmergencyGesture() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 810cecc..f6571ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -251,10 +251,9 @@
// App icon
ImageView appIcon = mViewHolder.getAppIcon();
if (data.getAppIcon() != null) {
- appIcon.setImageDrawable(data.getAppIcon());
+ appIcon.setImageIcon(data.getAppIcon());
} else {
- Drawable iconDrawable = mContext.getDrawable(R.drawable.ic_music_note);
- appIcon.setImageDrawable(iconDrawable);
+ appIcon.setImageResource(R.drawable.ic_music_note);
}
// Song name
@@ -332,7 +331,7 @@
int actionId = ACTION_IDS[i];
final ImageButton button = mViewHolder.getAction(actionId);
MediaAction mediaAction = actionIcons.get(i);
- button.setImageDrawable(mediaAction.getDrawable());
+ button.setImageIcon(mediaAction.getIcon());
button.setContentDescription(mediaAction.getContentDescription());
Runnable action = mediaAction.getAction();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 40a879a..0ed96ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -33,7 +33,7 @@
/**
* Icon shown on player, close to app name.
*/
- val appIcon: Drawable?,
+ val appIcon: Icon?,
/**
* Artist name.
*/
@@ -109,7 +109,7 @@
/** State of a media action. */
data class MediaAction(
- val drawable: Drawable?,
+ val icon: Icon?,
val action: Runnable?,
val contentDescription: CharSequence?
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index cb6b22c..5d63cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -455,7 +455,7 @@
val app = builder.loadHeaderAppName()
// App Icon
- val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
+ val smallIcon = sbn.notification.smallIcon
// Song name
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
@@ -501,8 +501,13 @@
} else {
null
}
+ val mediaActionIcon = if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) {
+ Icon.createWithResource(packageContext, action.getIcon()!!.getResId())
+ } else {
+ action.getIcon()
+ }
val mediaAction = MediaAction(
- action.getIcon().loadDrawable(packageContext),
+ mediaActionIcon,
runnable,
action.title)
actionIcons.add(mediaAction)
@@ -518,7 +523,7 @@
val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
val active = mediaEntries[key]?.active ?: true
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
- smallIconDrawable, artist, song, artWorkIcon, actionIcons,
+ smallIcon, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
active, resumeAction = resumeAction, isLocalSession = isLocalSession,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
@@ -572,7 +577,7 @@
val source = ImageDecoder.createSource(context.getContentResolver(), uri)
return try {
ImageDecoder.decodeBitmap(source) {
- decoder, info, source -> decoder.isMutableRequired = true
+ decoder, info, source -> decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
} catch (e: IOException) {
Log.e(TAG, "Unable to load bitmap", e)
@@ -612,7 +617,7 @@
private fun getResumeMediaAction(action: Runnable): MediaAction {
return MediaAction(
- context.getDrawable(R.drawable.lb_ic_play),
+ Icon.createWithResource(context, R.drawable.lb_ic_play),
action,
context.getString(R.string.controls_media_resume)
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 9b6a9ea..d1630eb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -44,6 +44,8 @@
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private ViewGroup mConnectedItem;
+
public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
}
@@ -79,18 +81,6 @@
return mController.getMediaDevices().size();
}
- void onItemClick(MediaDevice device) {
- mController.connectDevice(device);
- device.setState(MediaDeviceState.STATE_CONNECTING);
- notifyDataSetChanged();
- }
-
- void onItemClick(int customizedItem) {
- if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
- mController.launchBluetoothPairing();
- }
- }
-
@Override
CharSequence getItemTitle(MediaDevice device) {
if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
@@ -117,6 +107,10 @@
@Override
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
super.onBind(device, topMargin, bottomMargin);
+ final boolean currentlyConnected = isCurrentlyConnected(device);
+ if (currentlyConnected) {
+ mConnectedItem = mFrameLayout;
+ }
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
@@ -133,16 +127,16 @@
false /* showSeekBar*/, false /* showProgressBar */,
true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
- mFrameLayout.setOnClickListener(v -> onItemClick(device));
+ mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
} else if (!mController.hasAdjustVolumeUserRestriction()
- && isCurrentConnected(device)) {
+ && currentlyConnected) {
setTwoLineLayout(device, null /* title */, true /* bFocused */,
true /* showSeekBar*/, false /* showProgressBar */,
false /* showSubtitle */);
initSeekbar(device);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
- mFrameLayout.setOnClickListener(v -> onItemClick(device));
+ mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
}
}
}
@@ -160,5 +154,24 @@
mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
}
}
+
+ private void onItemClick(View view, MediaDevice device) {
+ if (mController.isTransferring()) {
+ return;
+ }
+
+ playSwitchingAnim(mConnectedItem, view);
+ mController.connectDevice(device);
+ device.setState(MediaDeviceState.STATE_CONNECTING);
+ if (!isAnimating()) {
+ notifyDataSetChanged();
+ }
+ }
+
+ private void onItemClick(int customizedItem) {
+ if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
+ mController.launchBluetoothPairing();
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 01dc6c4..2d3e77d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -16,6 +16,8 @@
package com.android.systemui.media.dialog;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Typeface;
import android.text.TextUtils;
@@ -33,6 +35,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
@@ -50,6 +53,7 @@
private boolean mIsDragging;
private int mMargin;
+ private boolean mIsAnimating;
Context mContext;
View mHolderView;
@@ -75,7 +79,7 @@
return device.getName();
}
- boolean isCurrentConnected(MediaDevice device) {
+ boolean isCurrentlyConnected(MediaDevice device) {
return TextUtils.equals(device.getId(),
mController.getCurrentConnectedMediaDevice().getId());
}
@@ -84,10 +88,17 @@
return mIsDragging;
}
+ boolean isAnimating() {
+ return mIsAnimating;
+ }
+
/**
* ViewHolder for binding device view.
*/
abstract class MediaDeviceBaseViewHolder extends RecyclerView.ViewHolder {
+
+ private static final int ANIM_DURATION = 200;
+
final FrameLayout mFrameLayout;
final TextView mTitleText;
final TextView mTwoLineTitleText;
@@ -123,17 +134,16 @@
private void setMargin(boolean topMargin, boolean bottomMargin) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
.getLayoutParams();
- if (topMargin) {
- params.topMargin = mMargin;
- }
- if (bottomMargin) {
- params.bottomMargin = mMargin;
- }
+ params.topMargin = topMargin ? mMargin : 0;
+ params.bottomMargin = bottomMargin ? mMargin : 0;
mFrameLayout.setLayoutParams(params);
}
+
void setSingleLineLayout(CharSequence title, boolean bFocused) {
- mTitleText.setVisibility(View.VISIBLE);
mTwoLineLayout.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.GONE);
+ mTitleText.setVisibility(View.VISIBLE);
+ mTitleText.setTranslationY(0);
mTitleText.setText(title);
if (bFocused) {
mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
@@ -146,9 +156,11 @@
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
+ mSeekBar.setAlpha(1);
mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
+ mTwoLineTitleText.setTranslationY(0);
if (device == null) {
mTwoLineTitleText.setText(title);
} else {
@@ -189,5 +201,53 @@
}
});
}
+
+ void playSwitchingAnim(@NonNull View from, @NonNull View to) {
+ final float delta = (float) (mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_title_anim_y_delta));
+ final SeekBar fromSeekBar = from.requireViewById(R.id.volume_seekbar);
+ final TextView toTitleText = to.requireViewById(R.id.title);
+ if (fromSeekBar.getVisibility() != View.VISIBLE || toTitleText.getVisibility()
+ != View.VISIBLE) {
+ return;
+ }
+ mIsAnimating = true;
+ // Animation for title text
+ toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ toTitleText.animate()
+ .setDuration(ANIM_DURATION)
+ .translationY(-delta)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ to.requireViewById(R.id.volume_indeterminate_progress).setVisibility(
+ View.VISIBLE);
+ }
+ });
+ // Animation for seek bar
+ fromSeekBar.animate()
+ .alpha(0)
+ .setDuration(ANIM_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final TextView fromTitleText = from.requireViewById(
+ R.id.two_line_title);
+ fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ fromTitleText.animate()
+ .setDuration(ANIM_DURATION)
+ .translationY(delta)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimating = false;
+ notifyDataSetChanged();
+ }
+ });
+ }
+ });
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index ebca8a7..3b82999 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -170,7 +170,7 @@
mHeaderSubtitle.setText(subTitle);
mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
}
- if (!mAdapter.isDragging()) {
+ if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
mAdapter.notifyDataSetChanged();
}
// Show when remote media session is available
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 6d6d6cb..1538e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -207,6 +207,7 @@
private boolean mUseMLModel;
private float mMLModelThreshold;
private String mPackageName;
+ private float mMLResults;
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
@@ -531,10 +532,10 @@
new long[]{(long) y},
};
- final float results = mBackGestureTfClassifierProvider.predict(featuresVector);
- if (results == -1) return -1;
+ mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+ if (mMLResults == -1) return -1;
- return results >= mMLModelThreshold ? 1 : 0;
+ return mMLResults >= mMLModelThreshold ? 1 : 0;
}
private boolean isWithinTouchRegion(int x, int y) {
@@ -611,7 +612,8 @@
(int) mDownPoint.x, (int) mDownPoint.y,
(int) mEndPoint.x, (int) mEndPoint.y,
mEdgeWidthLeft + mLeftInset,
- mDisplaySize.x - (mEdgeWidthRight + mRightInset));
+ mDisplaySize.x - (mEdgeWidthRight + mRightInset),
+ mUseMLModel ? mMLResults : -2);
}
private void onMotionEvent(MotionEvent ev) {
@@ -621,6 +623,7 @@
// either the bouncer is showing or the notification panel is hidden
mInputEventReceiver.setBatchingEnabled(false);
mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
+ mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index b0fcdf8..98e5794 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -262,10 +262,10 @@
if (!PipUtils.hasSystemFeature(context)) {
Log.w(TAG, "Device not support PIP feature");
- return;
+ } else {
+ mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
+ displayController.addDisplayWindowListener(this);
}
- mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
- displayController.addDisplayWindowListener(this);
}
public Handler getUpdateHandler() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
index 6ac4e4cb..d27f257 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
@@ -219,41 +219,38 @@
PipTaskOrganizer pipTaskOrganizer,
WindowManagerShellWrapper windowManagerShellWrapper
) {
- if (mInitialized) {
- return;
- }
+ if (!mInitialized) {
+ mInitialized = true;
+ mContext = context;
+ mPipNotification = new PipNotification(context, this);
+ mPipBoundsHandler = pipBoundsHandler;
+ // Ensure that we have the display info in case we get calls to update the bounds
+ // before the listener calls back
+ final DisplayInfo displayInfo = new DisplayInfo();
+ context.getDisplay().getDisplayInfo(displayInfo);
+ mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
- mInitialized = true;
- mContext = context;
- mPipNotification = new PipNotification(context, this);
- mPipBoundsHandler = pipBoundsHandler;
- // Ensure that we have the display info in case we get calls to update the bounds before the
- // listener calls back
- final DisplayInfo displayInfo = new DisplayInfo();
- context.getDisplay().getDisplayInfo(displayInfo);
- mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+ mResizeAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipResizeAnimationDuration);
+ mPipTaskOrganizer = pipTaskOrganizer;
+ mPipTaskOrganizer.registerPipTransitionCallback(this);
+ mActivityTaskManager = ActivityTaskManager.getService();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
- mResizeAnimationDuration = context.getResources()
- .getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipTaskOrganizer = pipTaskOrganizer;
- mPipTaskOrganizer.registerPipTransitionCallback(this);
- mActivityTaskManager = ActivityTaskManager.getService();
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
+ // Initialize the last orientation and apply the current configuration
+ Configuration initialConfig = mContext.getResources().getConfiguration();
+ mLastOrientation = initialConfig.orientation;
+ loadConfigurationsAndApply(initialConfig);
- // Initialize the last orientation and apply the current configuration
- Configuration initialConfig = mContext.getResources().getConfiguration();
- mLastOrientation = initialConfig.orientation;
- loadConfigurationsAndApply(initialConfig);
-
- mMediaSessionManager =
- (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- mWindowManagerShellWrapper = windowManagerShellWrapper;
- try {
- mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register pinned stack listener", e);
+ mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+ mWindowManagerShellWrapper = windowManagerShellWrapper;
+ try {
+ mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2e258d5..8fec5a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -30,6 +30,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.AlarmClock;
@@ -64,6 +65,8 @@
import com.android.systemui.DualToneHandler;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
@@ -151,6 +154,8 @@
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
+ private DemoModeController mDemoModeController;
+ private DemoMode mDemoModeReceiver;
private boolean mAllIndicatorsEnabled;
private boolean mMicCameraIndicatorsEnabled;
@@ -207,7 +212,7 @@
StatusBarIconController statusBarIconController,
ActivityStarter activityStarter, PrivacyItemController privacyItemController,
CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger, DemoModeController demoModeController) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
@@ -219,6 +224,7 @@
mCommandQueue = commandQueue;
mRingerModeTracker = ringerModeTracker;
mUiEventLogger = uiEventLogger;
+ mDemoModeController = demoModeController;
}
@Override
@@ -268,6 +274,7 @@
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
+ mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mDateView = findViewById(R.id.date);
mSpace = findViewById(R.id.space);
@@ -526,6 +533,7 @@
updateStatusText();
});
mStatusBarIconController.addIconGroup(mIconManager);
+ mDemoModeController.addCallback(mDemoModeReceiver);
requestApplyInsets();
}
@@ -606,6 +614,7 @@
setListening(false);
mRingerModeTracker.getRingerModeInternal().removeObservers(this);
mStatusBarIconController.removeIconGroup(mIconManager);
+ mDemoModeController.removeCallback(mDemoModeReceiver);
super.onDetachedFromWindow();
}
@@ -769,4 +778,33 @@
private boolean getChipEnabled() {
return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
}
+
+ private static class ClockDemoModeReceiver implements DemoMode {
+ private Clock mClockView;
+
+ @Override
+ public List<String> demoCommands() {
+ return List.of(COMMAND_CLOCK);
+ }
+
+ ClockDemoModeReceiver(Clock clockView) {
+ mClockView = clockView;
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ mClockView.dispatchDemoCommand(command, args);
+ }
+
+ @Override
+ public void onDemoModeStarted() {
+ mClockView.onDemoModeStarted();
+ }
+
+ @Override
+ public void onDemoModeFinished() {
+ mClockView.onDemoModeFinished();
+ }
+ }
+
}
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 500de2d..9320499 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
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
@@ -73,6 +74,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
@@ -97,7 +99,6 @@
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -260,6 +261,7 @@
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
private FooterDismissListener mFooterDismissListener;
+ private boolean mFlingAfterUpEvent;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -3789,6 +3791,13 @@
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
float currentOverScrollTop = getCurrentOverScrollAmount(true);
if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ mFlingAfterUpEvent = true;
+ setFinishScrollingCallback(() -> {
+ mFlingAfterUpEvent = false;
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ setFinishScrollingCallback(null);
+ });
fling(-initialVelocity);
} else {
onOverScrollFling(false, initialVelocity);
@@ -3840,6 +3849,10 @@
return true;
}
+ boolean isFlingAfterUpEvent() {
+ return mFlingAfterUpEvent;
+ }
+
@ShadeViewRefactor(RefactorComponent.INPUT)
protected boolean isInsideQsContainer(MotionEvent ev) {
return ev.getY() < mQsContainer.getBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 703c214..6820819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -19,6 +19,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
@@ -47,6 +48,7 @@
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -1556,6 +1558,14 @@
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+
+ // When swiping directly on the NSSL, this would only get an onTouchEvent.
+ // We log any touches other than down, which will be captured by onTouchEvent.
+ // In the intercept we only start tracing when it's not a down (otherwise that down
+ // would be duplicated when intercepted).
+ if (scrollWantsIt && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
return swipeWantsIt || scrollWantsIt || expandWantsIt;
}
@@ -1611,7 +1621,32 @@
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+ traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
}
+
+ private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
+ // Handle interaction jank monitor cases.
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 3d51854..54fb863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -168,7 +168,7 @@
int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
if (!mDrawableCache.contains(iconRes)) {
- mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
+ mDrawableCache.put(iconRes, getContext().getDrawable(iconRes));
}
return mDrawableCache.get(iconRes);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cd9cc07..3f636ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,8 @@
import static android.view.View.GONE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
@@ -62,6 +64,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
@@ -1139,6 +1142,7 @@
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mNotificationStackScrollLayoutController.cancelLongPress();
}
break;
@@ -1170,6 +1174,7 @@
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
mView.getParent().requestDisallowInterceptTouchEvent(true);
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
onQsExpansionStarted();
notifyExpandingFinished();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1202,6 +1207,19 @@
&& x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
}
+ private void traceQsJank(boolean startTracing, boolean wasCancelled) {
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (startTracing) {
+ monitor.begin(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ if (wasCancelled) {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ }
+ }
+ }
+
private void initDownStates(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mOnlyAffordanceInThisMotion = false;
@@ -1315,9 +1333,9 @@
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mBarState != KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
-
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mConflictingQsExpansionGesture = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1405,6 +1423,7 @@
return;
}
mExpectingSynthesizedDown = true;
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
onTrackingStarted();
updatePanelExpanded();
}
@@ -1474,6 +1493,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mInitialTouchY = y;
mInitialTouchX = x;
onQsExpansionStarted();
@@ -1513,6 +1533,9 @@
if (fraction != 0f || y >= mInitialTouchY) {
flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+ } else {
+ traceQsJank(false /* startTracing */,
+ event.getActionMasked() == MotionEvent.ACTION_CANCEL);
}
if (mQsVelocityTracker != null) {
mQsVelocityTracker.recycle();
@@ -1893,7 +1916,7 @@
* @see #flingSettings(float, int, Runnable, boolean)
*/
public void flingSettings(float vel, int type) {
- flingSettings(vel, type, null, false /* isClick */);
+ flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */);
}
/**
@@ -1923,6 +1946,7 @@
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */);
return;
}
@@ -1947,12 +1971,18 @@
setQsExpansion((Float) animation.getAnimatedValue());
});
animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCanceled;
@Override
public void onAnimationStart(Animator animation) {
notifyExpandingStarted();
}
@Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
mAnimatingQS = false;
notifyExpandingFinished();
@@ -1961,6 +1991,7 @@
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */);
}
});
// Let's note that we're animating QS. Moving the animator here will cancel it immediately,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index bc80a1a..a4fc3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -49,6 +49,7 @@
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.R;
@@ -145,6 +146,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setWillNotDraw(!DEBUG);
+ InteractionJankMonitor.getInstance().init(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 6fa99ba..5a01f47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -40,6 +41,7 @@
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.DejankUtils;
@@ -109,6 +111,7 @@
private boolean mMotionAborted;
private boolean mUpwardsWhenThresholdReached;
private boolean mAnimatingOnDown;
+ private boolean mHandlingPointerUp;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -356,6 +359,9 @@
protected void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
+ if (!mHandlingPointerUp) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
@@ -571,6 +577,7 @@
target = getMaxPanelHeight() - getClearAllHeightWithPadding();
}
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
return;
}
@@ -622,7 +629,12 @@
}
setAnimator(null);
if (!mCancelled) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
+ } else {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
notifyBarPanelExpansionChanged();
}
@@ -1272,7 +1284,9 @@
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
+ mHandlingPointerUp = true;
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
+ mHandlingPointerUp = false;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -1330,6 +1344,12 @@
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ } else {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
break;
}
return !mGestureWaitForTouchSlop || mTracking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
index 7a78c15..0bd3624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
@@ -58,6 +58,10 @@
if (!mNotificationHandlerPackage.isEmpty()) {
startNotificationHandlerActivity(
new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
+ } else {
+ Log.w(TAG,
+ "Not toggling notification panel: config_notificationHandlerPackage is "
+ + "empty");
}
}
@@ -66,6 +70,10 @@
if (!mNotificationHandlerPackage.isEmpty()) {
startNotificationHandlerActivity(
new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
+ } else {
+ Log.w(TAG,
+ "Not expanding notification panel: config_notificationHandlerPackage is "
+ + "empty");
}
}
@@ -77,6 +85,9 @@
NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
closeNotificationIntent.setPackage(mNotificationHandlerPackage);
mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
+ } else {
+ Log.w(TAG,
+ "Not closing notification panel: config_notificationHandlerPackage is empty");
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 0e376bd..2d460aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -192,6 +192,7 @@
public void onItemClick_clickDevice_verifyConnectDevice() {
assertThat(mMediaDevice2.getState()).isEqualTo(
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
mViewHolder.mFrameLayout.performClick();
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
index 2465fe0..e7eeb30 100644
--- a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
- <string name="config_icon_mask" translatable="false">"MM55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
+ <string name="config_icon_mask" translatable="false">"M55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
<!-- Flag indicating whether round icons should be parsed from the application manifest. -->
<bool name="config_useRoundIcon">false</bool>
<!-- Corner radius of system dialogs -->
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f056df..ffdcd15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17334,4 +17334,18 @@
throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state ");
}
}
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * Requires permission identity of the shell UID.
+ */
+ @Override
+ public void holdLock(int durationMs) {
+ enforceCallingPermission(Manifest.permission.INJECT_EVENTS, "holdLock");
+
+ synchronized (this) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 1bf62a0..58ac2dc 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -733,10 +733,6 @@
uidRec.reset();
}
- if (mService.mAtmInternal != null) {
- mService.mAtmInternal.rankTaskLayersIfNeeded();
- }
-
mAdjSeq++;
if (fullUpdate) {
mNewNumServiceProcs = 0;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2dced8d..ebc5b59 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1909,8 +1909,8 @@
}
callback.initialize(this, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
processCurTop);
- final int minLayer = getWindowProcessController().computeOomAdjFromActivities(
- ProcessList.VISIBLE_APP_LAYER_MAX, callback);
+ final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
+ getWindowProcessController().computeOomAdjFromActivities(callback));
mCachedAdj = callback.adj;
mCachedForegroundActivities = callback.foregroundActivities;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 4a4c39d..cc8a330 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -46,6 +46,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
+import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.media.AudioManager;
@@ -189,6 +190,10 @@
private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
private int mNextVibratorTokenValue;
+ // State for lid switch
+ private final Object mLidSwitchLock = new Object();
+ private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
+
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
IInputFilter mInputFilter; // guarded by mInputFilterLock
@@ -330,6 +335,9 @@
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ /** Indicates an open state for the lid switch. */
+ public static final int SW_STATE_LID_OPEN = 0;
+
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -353,13 +361,33 @@
}
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
+ if (mWindowManagerCallbacks != null) {
+ unregisterLidSwitchCallbackInternal(mWindowManagerCallbacks);
+ }
mWindowManagerCallbacks = callbacks;
+ registerLidSwitchCallbackInternal(mWindowManagerCallbacks);
}
public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
mWiredAccessoryCallbacks = callbacks;
}
+ void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ boolean lidOpen;
+ synchronized (mLidSwitchLock) {
+ mLidSwitchCallbacks.add(callback);
+ lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+ == SW_STATE_LID_OPEN;
+ }
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
+ }
+
+ void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ synchronized (mLidSwitchLock) {
+ mLidSwitchCallbacks.remove(callback);
+ }
+ }
+
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
@@ -1934,6 +1962,7 @@
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */}
+ synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
nativeMonitor(mPtr);
}
@@ -1964,7 +1993,15 @@
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
- mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+
+ ArrayList<LidSwitchCallback> callbacksCopy;
+ synchronized (mLidSwitchLock) {
+ callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+ }
+ for (int i = 0; i < callbacksCopy.size(); i++) {
+ LidSwitchCallback callbacks = callbacksCopy.get(i);
+ callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
}
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
@@ -2263,20 +2300,13 @@
/**
* Callback interface implemented by the Window Manager.
*/
- public interface WindowManagerCallbacks {
+ public interface WindowManagerCallbacks extends LidSwitchCallback {
/**
* This callback is invoked when the confuguration changes.
*/
public void notifyConfigurationChanged();
/**
- * This callback is invoked when the lid switch changes state.
- * @param whenNanos the time when the change occurred
- * @param lidOpen true if the lid is open
- */
- public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
-
- /**
* This callback is invoked when the camera lens cover switch changes state.
* @param whenNanos the time when the change occurred
* @param lensCovered true is the lens is covered
@@ -2603,6 +2633,16 @@
@NonNull IBinder toChannelToken) {
return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
}
+
+ @Override
+ public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
+ registerLidSwitchCallbackInternal(callbacks);
+ }
+
+ @Override
+ public void unregisterLidSwitchCallback(LidSwitchCallback callbacks) {
+ unregisterLidSwitchCallbackInternal(callbacks);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a44fabbe..2eccaf1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -17,6 +17,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.CLIENTS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ELAPSED_REALTIME_NANOS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.ENTRY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -96,12 +99,15 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.ContextThemeWrapper;
import android.view.DisplayInfo;
import android.view.IWindowManager;
@@ -4032,6 +4038,55 @@
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
}
+ /**
+ * Starting point for dumping the IME tracing information in proto format.
+ *
+ * @param clientProtoDump dump information from the IME client side
+ */
+ @BinderThread
+ @Override
+ public void startProtoDump(byte[] clientProtoDump) {
+ if (!ImeTracing.getInstance().isAvailable() || !ImeTracing.getInstance().isEnabled()) {
+ return;
+ }
+ if (clientProtoDump == null && mCurClient == null) {
+ return;
+ }
+
+ ProtoOutputStream proto = new ProtoOutputStream();
+ final long token = proto.start(ENTRY);
+ proto.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+ // TODO: get server side dump
+ if (clientProtoDump != null) {
+ proto.write(CLIENTS, clientProtoDump);
+ } else {
+ IBinder client = null;
+
+ synchronized (mMethodMap) {
+ if (mCurClient != null && mCurClient.client != null) {
+ client = mCurClient.client.asBinder();
+ }
+ }
+
+ if (client != null) {
+ try {
+ proto.write(CLIENTS,
+ TransferPipe.dumpAsync(client, ImeTracing.PROTO_ARG));
+ } catch (IOException | RemoteException e) {
+ Log.e(TAG, "Exception while collecting client side ime dump", e);
+ }
+ }
+ }
+ proto.end(token);
+ ImeTracing.getInstance().addToBuffer(proto);
+ }
+
+ @BinderThread
+ @Override
+ public boolean isImeTraceEnabled() {
+ return ImeTracing.getInstance().isEnabled();
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -5426,6 +5481,21 @@
return mService.handleShellCommandSetInputMethod(this);
case "reset":
return mService.handleShellCommandResetInputMethod(this);
+ case "tracing":
+ int result = ImeTracing.getInstance().onShellCommand(this);
+ boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
+ for (ClientState state : mService.mClients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(isImeTraceEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "Error while trying to enable/disable ime "
+ + "trace on client window", e);
+ }
+ }
+ }
+ return result;
default:
getOutPrintWriter().println("Unknown command: " + imeCommand);
return ShellCommandResult.FAILURE;
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index b518eb1..a6ca25b 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1805,5 +1805,16 @@
mUserDataMap.dump(fd, ipw, args);
}
}
+
+ @BinderThread
+ @Override
+ public void startProtoDump(byte[] clientProtoDump) throws RemoteException {
+ }
+
+ @BinderThread
+ @Override
+ public boolean isImeTraceEnabled() throws RemoteException {
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e7304b..1ae1681 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6214,8 +6214,11 @@
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
+ if (p == null) {
+ return false;
+ }
final PackageSetting ps = getPackageSetting(p.getPackageName());
- if (p == null || ps == null) {
+ if (ps == null) {
return false;
}
final int callingUid = Binder.getCallingUid();
@@ -25949,6 +25952,15 @@
mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writeLPr();
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+ synchronized (mLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 35b1449..df283e2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1552,6 +1552,17 @@
startActivityAsUser(intent, UserHandle.CURRENT);
}
+ private void toggleNotificationPanel() {
+ IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ try {
+ statusBarService.togglePanel();
+ } catch (RemoteException e) {
+ // do nothing.
+ }
+ }
+ }
+
private void showPictureInPictureMenu(KeyEvent event) {
if (DEBUG_INPUT) Log.d(TAG, "showPictureInPictureMenu event=" + event);
mHandler.removeMessages(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
@@ -1696,14 +1707,7 @@
launchAssistAction(null, deviceId);
break;
case LONG_PRESS_HOME_NOTIFICATION_PANEL:
- IStatusBarService statusBarService = getStatusBarService();
- if (statusBarService != null) {
- try {
- statusBarService.togglePanel();
- } catch (RemoteException e) {
- // do nothing.
- }
- }
+ toggleNotificationPanel();
break;
default:
Log.w(TAG, "Undefined long press on home behavior: "
@@ -2807,6 +2811,11 @@
msg.sendToTarget();
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_NOTIFICATION) {
+ if (!down) {
+ toggleNotificationPanel();
+ }
+ return -1;
}
// Toggle Caps Lock on META-ALT.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6792430..1cb610f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4465,6 +4465,9 @@
}
detachChildren();
}
+ if (app != null) {
+ app.invalidateOomScoreReferenceState(false /* computeNow */);
+ }
switch (state) {
case RESUMED:
@@ -7864,7 +7867,7 @@
InsetUtils.addInsets(insets, getLetterboxInsets());
return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
record.mAdapter.mCapturedLeash, !fillsParent(),
- mainWindow.mWinAnimator.mLastClipRect, insets,
+ new Rect(), insets,
getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds,
record.mAdapter.mStackBounds, task.getWindowConfiguration(),
false /*isNotInRecents*/,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 2dc22ec..e65be41 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -499,9 +499,6 @@
/** @return the process for the top-most resumed activity in the system. */
public abstract WindowProcessController getTopApp();
- /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
- public abstract void rankTaskLayersIfNeeded();
-
/** Destroy all activities. */
public abstract void scheduleDestroyAllActivities(String reason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 15669cb..0402140 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -666,7 +666,7 @@
*/
CompatModePackages mCompatModePackages;
- private FontScaleSettingObserver mFontScaleSettingObserver;
+ private SettingObserver mSettingsObserver;
WindowOrganizerController mWindowOrganizerController;
TaskOrganizerController mTaskOrganizerController;
@@ -676,16 +676,19 @@
private int mDeviceOwnerUid = Process.INVALID_UID;
- private final class FontScaleSettingObserver extends ContentObserver {
+ private final class SettingObserver extends ContentObserver {
private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
+ private final Uri mForceBoldTextUri = Settings.Secure.getUriFor(
+ Settings.Secure.FORCE_BOLD_TEXT);
- public FontScaleSettingObserver() {
+ SettingObserver() {
super(mH);
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mForceBoldTextUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -698,6 +701,8 @@
synchronized (mGlobalLock) {
updateShouldShowDialogsLocked(getGlobalConfiguration());
}
+ } else if (mForceBoldTextUri.equals(uri)) {
+ updateForceBoldTextIfNeeded(userId);
}
}
}
@@ -757,7 +762,7 @@
}
public void installSystemProviders() {
- mFontScaleSettingObserver = new FontScaleSettingObserver();
+ mSettingsObserver = new SettingObserver();
}
public void retrieveSettings(ContentResolver resolver) {
@@ -5362,6 +5367,20 @@
}
}
+ private void updateForceBoldTextIfNeeded(@UserIdInt int userId) {
+ final int forceBoldTextConfig = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FORCE_BOLD_TEXT, Configuration.FORCE_BOLD_TEXT_UNDEFINED, userId);
+ synchronized (mGlobalLock) {
+ if (getGlobalConfiguration().forceBoldText == forceBoldTextConfig) {
+ return;
+ }
+ final Configuration configuration =
+ mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ configuration.forceBoldText = forceBoldTextConfig;
+ updatePersistentConfiguration(configuration, userId);
+ }
+ }
+
// Actually is sleeping or shutting down or whatever else in the future
// is an inactive state.
boolean isSleepingOrShuttingDownLocked() {
@@ -7187,16 +7206,6 @@
}
}
- @HotPath(caller = HotPath.OOM_ADJUSTMENT)
- @Override
- public void rankTaskLayersIfNeeded() {
- synchronized (mGlobalLockWithoutBoost) {
- if (mRootWindowContainer != null) {
- mRootWindowContainer.rankTaskLayersIfNeeded();
- }
- }
- }
-
@Override
public void scheduleDestroyAllActivities(String reason) {
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b50cb4c..42a20ff 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -929,7 +929,7 @@
? MODE_OPENING
: MODE_CLOSING;
mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
- !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
+ !topApp.fillsParent(), new Rect(),
insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
mLocalBounds, mBounds, mTask.getWindowConfiguration(),
mIsRecentTaskInvisible, null, null);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 71ecf72..8b52b58 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -276,6 +276,8 @@
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
private int mTmpTaskLayerRank;
+ private final ArraySet<WindowProcessController> mTmpTaskLayerChangedProcs = new ArraySet<>();
+ private final LockedScheduler mRankTaskLayersScheduler;
private boolean mTmpBoolean;
private RemoteException mTmpRemoteException;
@@ -450,6 +452,12 @@
mStackSupervisor = mService.mStackSupervisor;
mStackSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
+ mRankTaskLayersScheduler = new LockedScheduler(mService) {
+ @Override
+ public void execute() {
+ rankTaskLayersIfNeeded();
+ }
+ };
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -2698,27 +2706,39 @@
void invalidateTaskLayers() {
mTaskLayersChanged = true;
+ mRankTaskLayersScheduler.scheduleIfNeeded();
}
+ /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
void rankTaskLayersIfNeeded() {
if (!mTaskLayersChanged) {
return;
}
mTaskLayersChanged = false;
mTmpTaskLayerRank = 0;
- final PooledConsumer c = PooledLambda.obtainConsumer(
- RootWindowContainer::rankTaskLayerForActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
- }
+ // Only rank for leaf tasks because the score of activity is based on immediate parent.
+ forAllLeafTasks(task -> {
+ final int oldRank = task.mLayerRank;
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (r != null && r.mVisibleRequested) {
+ task.mLayerRank = ++mTmpTaskLayerRank;
+ } else {
+ task.mLayerRank = Task.LAYER_RANK_INVISIBLE;
+ }
+ if (task.mLayerRank != oldRank) {
+ task.forAllActivities(activity -> {
+ if (activity.hasProcess()) {
+ mTmpTaskLayerChangedProcs.add(activity.app);
+ }
+ });
+ }
+ }, true /* traverseTopToBottom */);
- private void rankTaskLayerForActivity(ActivityRecord r) {
- if (r.canBeTopRunning() && r.mVisibleRequested) {
- r.getTask().mLayerRank = ++mTmpTaskLayerRank;
- } else {
- r.getTask().mLayerRank = -1;
+ for (int i = mTmpTaskLayerChangedProcs.size() - 1; i >= 0; i--) {
+ mTmpTaskLayerChangedProcs.valueAt(i).invalidateOomScoreReferenceState(
+ true /* computeNow */);
}
+ mTmpTaskLayerChangedProcs.clear();
}
void clearOtherAppTimeTrackers(AppTimeTracker except) {
@@ -3680,4 +3700,34 @@
+ ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
}
}
+
+ /**
+ * Helper class to schedule the runnable if it hasn't scheduled on display thread inside window
+ * manager lock.
+ */
+ abstract static class LockedScheduler implements Runnable {
+ private final ActivityTaskManagerService mService;
+ private boolean mScheduled;
+
+ LockedScheduler(ActivityTaskManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mService.mGlobalLock) {
+ mScheduled = false;
+ execute();
+ }
+ }
+
+ abstract void execute();
+
+ void scheduleIfNeeded() {
+ if (!mScheduled) {
+ mService.mH.post(this);
+ mScheduled = true;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ce602de..9be4ace 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -464,9 +464,10 @@
int mMinWidth;
int mMinHeight;
+ static final int LAYER_RANK_INVISIBLE = -1;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
// This number will be assigned when we evaluate OOM scores for all visible tasks.
- int mLayerRank = -1;
+ int mLayerRank = LAYER_RANK_INVISIBLE;
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
@@ -1539,7 +1540,6 @@
if (isPersistable) {
mLastTimeMoved = System.currentTimeMillis();
}
- mRootWindowContainer.invalidateTaskLayers();
}
// Close up recents linked list.
@@ -7452,6 +7452,10 @@
if (!mChildren.contains(child)) {
return;
}
+ if (child.asTask() != null) {
+ // Non-root task position changed.
+ mRootWindowContainer.invalidateTaskLayers();
+ }
final boolean isTop = getTopChild() == child;
if (isTop) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 76bd6ce..55e6a78 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -441,6 +441,12 @@
}
@Override
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+ mRootWindowContainer.invalidateTaskLayers();
+ }
+
+ @Override
boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
boolean traverseTopToBottom) {
return callback.apply(this);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7d293dd..d434bf9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -411,7 +411,7 @@
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
*/
static boolean sDisableCustomTaskAnimationProperty =
- SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, false);
+ SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -2423,8 +2423,8 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (winAnimator.mSurfaceController != null) {
- outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
- winAnimator.mSurfaceController.getHeight());
+ win.calculateSurfaceBounds(win.getAttrs(), mTmpRect);
+ outSurfaceSize.set(mTmpRect.width(), mTmpRect.height());
}
getInsetsSourceControls(win, outActiveControls);
}
@@ -8247,4 +8247,14 @@
embeddedWindow.getName(), grantFocus);
}
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+
+ synchronized (mGlobalLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 268281b..4b8a398 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -221,6 +221,9 @@
@Nullable
private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+ /** The state for oom-adjustment calculation. */
+ private final OomScoreReferenceState mOomRefState;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
@@ -232,6 +235,7 @@
mAtm = atm;
mDisplayId = INVALID_DISPLAY;
mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
+ mOomRefState = new OomScoreReferenceState(this);
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
@@ -688,15 +692,7 @@
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean hasVisibleActivities() {
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
- if (r.mVisibleRequested) {
- return true;
- }
- }
- }
- return false;
+ return (mOomRefState.mActivityStateFlags & OomScoreReferenceState.FLAG_IS_VISIBLE) != 0;
}
@HotPath(caller = HotPath.LRU_UPDATE)
@@ -991,6 +987,34 @@
mHostActivities.remove(r);
}
+ private static class OomScoreReferenceState extends RootWindowContainer.LockedScheduler {
+ private static final int FLAG_IS_VISIBLE = 0x10000000;
+ private static final int FLAG_IS_PAUSING = 0x20000000;
+ private static final int FLAG_IS_STOPPING = 0x40000000;
+ private static final int FLAG_IS_STOPPING_FINISHING = 0x80000000;
+ /** @see Task#mLayerRank */
+ private static final int MASK_MIN_TASK_LAYER = 0x0000ffff;
+
+ private final WindowProcessController mOwner;
+ boolean mChanged;
+
+ /**
+ * The higher 16 bits are the activity states, and the lower 16 bits are the task layer
+ * rank. This field is written by window manager and read by activity manager.
+ */
+ volatile int mActivityStateFlags = MASK_MIN_TASK_LAYER;
+
+ OomScoreReferenceState(WindowProcessController owner) {
+ super(owner.mAtm);
+ mOwner = owner;
+ }
+
+ @Override
+ public void execute() {
+ mOwner.computeOomScoreReferenceStateIfNeeded();
+ }
+ }
+
public interface ComputeOomAdjCallback {
void onVisibleActivity();
void onPausedActivity();
@@ -998,64 +1022,102 @@
void onOtherActivity();
}
+ /**
+ * Returns the minimum task layer rank. It should only be called if {@link #hasActivities}
+ * returns {@code true}.
+ */
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
- public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+ public int computeOomAdjFromActivities(ComputeOomAdjCallback callback) {
+ final int flags = mOomRefState.mActivityStateFlags;
+ if ((flags & OomScoreReferenceState.FLAG_IS_VISIBLE) != 0) {
+ callback.onVisibleActivity();
+ } else if ((flags & OomScoreReferenceState.FLAG_IS_PAUSING) != 0) {
+ callback.onPausedActivity();
+ } else if ((flags & OomScoreReferenceState.FLAG_IS_STOPPING) != 0) {
+ callback.onStoppingActivity(
+ (flags & OomScoreReferenceState.FLAG_IS_STOPPING_FINISHING) != 0);
+ } else {
+ callback.onOtherActivity();
+ }
+ return flags & OomScoreReferenceState.MASK_MIN_TASK_LAYER;
+ }
+
+ void computeOomScoreReferenceStateIfNeeded() {
+ if (!mOomRefState.mChanged) {
+ return;
+ }
+ mOomRefState.mChanged = false;
+
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
Task.ActivityState best = DESTROYED;
boolean finishing = true;
boolean visible = false;
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- final int activitiesSize = mActivities.size();
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = mActivities.get(j);
- if (r.app != this) {
- Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
- + " instead of expected " + this);
- if (r.app == null || (r.app.mUid == mUid)) {
- // Only fix things up when they look sane
- r.setProcess(this);
- } else {
- continue;
- }
- }
- if (r.mVisibleRequested) {
- final Task task = r.getTask();
- if (task != null && minTaskLayer > 0) {
- final int layer = task.mLayerRank;
- if (layer >= 0 && minTaskLayer > layer) {
- minTaskLayer = layer;
- }
- }
- visible = true;
- // continue the loop, in case there are multiple visible activities in
- // this process, we'd find out the one with the minimal layer, thus it'll
- // get a higher adj score.
+ int minTaskLayer = Integer.MAX_VALUE;
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.app != this) {
+ Slog.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ + " instead of expected " + this);
+ if (r.app == null || (r.app.mUid == mUid)) {
+ // Only fix things up when they look valid.
+ r.setProcess(this);
} else {
- if (best != PAUSING && best != PAUSED) {
- if (r.isState(PAUSING, PAUSED)) {
- best = PAUSING;
- } else if (r.isState(STOPPING)) {
- best = STOPPING;
- // Not "finishing" if any of activity isn't finishing.
- finishing &= r.finishing;
- }
- }
+ continue;
}
}
- }
- if (visible) {
- callback.onVisibleActivity();
- } else if (best == PAUSING) {
- callback.onPausedActivity();
- } else if (best == STOPPING) {
- callback.onStoppingActivity(finishing);
- } else {
- callback.onOtherActivity();
- }
+ if (r.mVisibleRequested) {
+ final Task task = r.getTask();
+ if (task != null && minTaskLayer > 0) {
+ final int layer = task.mLayerRank;
+ if (layer >= 0 && minTaskLayer > layer) {
+ minTaskLayer = layer;
+ }
+ }
+ visible = true;
+ // continue the loop, in case there are multiple visible activities in
+ // this process, we'd find out the one with the minimal layer, thus it'll
+ // get a higher adj score.
+ } else if (best != PAUSING && best != PAUSED) {
+ if (r.isState(PAUSING, PAUSED)) {
+ best = PAUSING;
+ } else if (r.isState(STOPPING)) {
+ best = STOPPING;
+ // Not "finishing" if any of activity isn't finishing.
+ finishing &= r.finishing;
+ }
+ }
- return minTaskLayer;
+ int stateFlags = minTaskLayer & OomScoreReferenceState.MASK_MIN_TASK_LAYER;
+ if (visible) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_VISIBLE;
+ } else if (best == PAUSING) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_PAUSING;
+ } else if (best == STOPPING) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_STOPPING;
+ if (finishing) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_STOPPING_FINISHING;
+ }
+ }
+ mOomRefState.mActivityStateFlags = stateFlags;
+ }
+ }
+
+ void invalidateOomScoreReferenceState(boolean computeNow) {
+ mOomRefState.mChanged = true;
+ if (computeNow) {
+ computeOomScoreReferenceStateIfNeeded();
+ return;
+ }
+ mOomRefState.scheduleIfNeeded();
+ }
+
+ /** Called when the process has some oom related changes and it is going to update oom-adj. */
+ private void prepareOomAdjustment() {
+ mAtm.mRootWindowContainer.rankTaskLayersIfNeeded();
+ // The task layer may not change but the activity state in the same task may change.
+ computeOomScoreReferenceStateIfNeeded();
}
public int computeRelaunchReason() {
@@ -1097,6 +1159,9 @@
if (addPendingTopUid) {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
}
+ if (updateOomAdj) {
+ prepareOomAdjustment();
+ }
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
mListener, updateServiceConnectionActivities, activityChange, updateOomAdj);
@@ -1158,6 +1223,7 @@
if (topProcessState == ActivityManager.PROCESS_STATE_TOP) {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
}
+ prepareOomAdjustment();
// Posting the message at the front of queue so WM lock isn't held when we call into AM,
// and the process state of starting activity can be updated quicker which will give it a
// higher scheduling group.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 422e6f2..ad28177 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1409,15 +1409,14 @@
// Add a window that is using blastSync to the resizing list if it hasn't been reported
// already. This because the window is waiting on a finishDrawing from the client.
if (didFrameInsetsChange
- || winAnimator.mSurfaceResized
|| configChanged
|| dragResizingChanged
|| mReportOrientationChanged
|| shouldSendRedrawForSync()) {
ProtoLog.v(WM_DEBUG_RESIZE,
- "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b "
+ "Resize reasons for w=%s: %s configChanged=%b "
+ "dragResizingChanged=%b reportOrientationChanged=%b",
- this, mWindowFrames.getInsetsChangedInfo(), winAnimator.mSurfaceResized,
+ this, mWindowFrames.getInsetsChangedInfo(),
configChanged, dragResizingChanged, mReportOrientationChanged);
// If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -3633,7 +3632,6 @@
// that may cause WINDOW_FREEZE_TIMEOUT because resizing the client keeps failing.
mReportOrientationChanged = false;
mDragResizingChangeReported = true;
- mWinAnimator.mSurfaceResized = false;
mWindowFrames.resetInsetsChanged();
final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
@@ -4952,93 +4950,6 @@
}
/**
- * Calculate the window crop according to system decor policy. In general this is
- * the system decor rect (see #calculateSystemDecorRect), but we also have some
- * special cases. This rectangle is in screen space.
- */
- void calculatePolicyCrop(Rect policyCrop) {
- final DisplayContent displayContent = getDisplayContent();
-
- if (!displayContent.isDefaultDisplay && !displayContent.supportsSystemDecorations()) {
- // On a different display there is no system decor. Crop the window
- // by the screen boundaries.
- final DisplayInfo displayInfo = getDisplayInfo();
- policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
- mWindowFrames.mCompatFrame.height());
- policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
- displayInfo.logicalWidth - mWindowFrames.mCompatFrame.left,
- displayInfo.logicalHeight - mWindowFrames.mCompatFrame.top);
- } else if (skipDecorCrop()) {
- // Windows without policy decor aren't cropped.
- policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
- mWindowFrames.mCompatFrame.height());
- } else {
- // Crop to the system decor specified by policy.
- calculateSystemDecorRect(policyCrop);
- }
- }
-
- /**
- * The system decor rect is the region of the window which is not covered
- * by system decorations.
- */
- private void calculateSystemDecorRect(Rect systemDecorRect) {
- final Rect decorRect = mWindowFrames.mDecorFrame;
- final int width = mWindowFrames.mFrame.width();
- final int height = mWindowFrames.mFrame.height();
-
- final int left = mWindowFrames.mFrame.left;
- final int top = mWindowFrames.mFrame.top;
-
- // Initialize the decor rect to the entire frame.
- if (isDockedResizing()) {
- // If we are resizing with the divider, the task bounds might be smaller than the
- // stack bounds. The system decor is used to clip to the task bounds, which we don't
- // want in this case in order to avoid holes.
- //
- // We take care to not shrink the width, for surfaces which are larger than
- // the display region. Of course this area will not eventually be visible
- // but if we truncate the width now, we will calculate incorrectly
- // when adjusting to the stack bounds.
- final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
- systemDecorRect.set(0, 0,
- Math.max(width, displayInfo.logicalWidth),
- Math.max(height, displayInfo.logicalHeight));
- } else {
- systemDecorRect.set(0, 0, width, height);
- }
-
- // If a freeform window is animating from a position where it would be cutoff, it would be
- // cutoff during the animation. We don't want that, so for the duration of the animation
- // we ignore the decor cropping and depend on layering to position windows correctly.
-
- // We also ignore cropping when the window is currently being drag resized in split screen
- // to prevent issues with the crop for screenshot.
- final boolean cropToDecor =
- !(inFreeformWindowingMode() && isAnimatingLw()) && !isDockedResizing();
- if (cropToDecor) {
- // Intersect with the decor rect, offsetted by window position.
- systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
- decorRect.right - left, decorRect.bottom - top);
- }
-
- // If size compatibility is being applied to the window, the
- // surface is scaled relative to the screen. Also apply this
- // scaling to the crop rect. We aren't using the standard rect
- // scale function because we want to round things to make the crop
- // always round to a larger rect to ensure we don't crop too
- // much and hide part of the window that should be seen.
- if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
- final float scale = mInvGlobalScale;
- systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
- systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
- systemDecorRect.right = (int) ((systemDecorRect.right + 1) * scale - 0.5f);
- systemDecorRect.bottom = (int) ((systemDecorRect.bottom + 1) * scale - 0.5f);
- }
-
- }
-
- /**
* Expand the given rectangle by this windows surface insets. This
* takes you from the 'window size' to the 'surface size'.
* The surface insets are positive in each direction, so we inset by
@@ -5095,9 +5006,10 @@
// on the new one. This prevents blinking when we change elevation of freeform and
// pinned windows.
if (!mWinAnimator.tryChangeFormatInPlaceLocked()) {
- mWinAnimator.preserveSurfaceLocked();
+ mWinAnimator.preserveSurfaceLocked(getPendingTransaction());
result |= RELAYOUT_RES_SURFACE_CHANGED
| RELAYOUT_RES_FIRST_TIME;
+ scheduleAnimation();
}
}
@@ -5113,9 +5025,10 @@
// to preserve and destroy windows which are attached to another, they
// will keep their surface and its size may change over time.
if (mHasSurface && !isChildWindow()) {
- mWinAnimator.preserveSurfaceLocked();
+ mWinAnimator.preserveSurfaceLocked(getPendingTransaction());
result |= RELAYOUT_RES_SURFACE_CHANGED |
RELAYOUT_RES_FIRST_TIME;
+ scheduleAnimation();
}
}
final boolean freeformResizing = isDragResizing()
@@ -5939,4 +5852,37 @@
void requestRedrawForSync() {
mRedrawForSyncReported = false;
}
+
+ void calculateSurfaceBounds(WindowManager.LayoutParams attrs, Rect outSize) {
+ outSize.setEmpty();
+ if ((attrs.flags & FLAG_SCALED) != 0) {
+ // For a scaled surface, we always want the requested size.
+ outSize.right = mRequestedWidth;
+ outSize.bottom = mRequestedHeight;
+ } else {
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ if (isDragResizing()) {
+ final DisplayInfo displayInfo = getDisplayInfo();
+ outSize.right = displayInfo.logicalWidth;
+ outSize.bottom = displayInfo.logicalHeight;
+ } else {
+ getCompatFrameSize(outSize);
+ }
+ }
+
+ // This doesn't necessarily mean that there is an error in the system. The sizes might be
+ // incorrect, because it is before the first layout or draw.
+ if (outSize.width() < 1) {
+ outSize.right = 1;
+ }
+ if (outSize.height() < 1) {
+ outSize.bottom = 1;
+ }
+
+ // Adjust for surface insets.
+ outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
+ -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 029c158..2ace23f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -121,12 +121,6 @@
boolean mAnimationIsEntrance;
- /**
- * Set when we have changed the size of the surface, to know that
- * we must tell them application to resize (and thus redraw itself).
- */
- boolean mSurfaceResized;
-
WindowSurfaceController mSurfaceController;
private WindowSurfaceController mPendingDestroySurface;
@@ -141,9 +135,6 @@
float mAlpha = 0;
float mLastAlpha = 0;
- Rect mTmpClipRect = new Rect();
- Rect mLastClipRect = new Rect();
- Rect mLastFinalClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
private Rect mTmpAnimatingBounds = new Rect();
private Rect mTmpSourceBounds = new Rect();
@@ -358,7 +349,7 @@
return result;
}
- void preserveSurfaceLocked() {
+ void preserveSurfaceLocked(SurfaceControl.Transaction t) {
if (mDestroyPreservedSurfaceUponRedraw) {
// This could happen when switching the surface mode very fast. For example,
// we preserved a surface when dragResizing changed to true. Then before the
@@ -385,7 +376,7 @@
// Our SurfaceControl is always at layer 0 within the parent Surface managed by
// window-state. We want this old Surface to stay on top of the new one
// until we do the swap, so we place it at a positive layer.
- mSurfaceController.mSurfaceControl.setLayer(PRESERVED_SURFACE_LAYER);
+ t.setLayer(mSurfaceController.getClientViewRootSurface(), PRESERVED_SURFACE_LAYER);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
@@ -462,7 +453,8 @@
flags |= SurfaceControl.SKIP_SCREENSHOT;
}
- calculateSurfaceBounds(w, attrs, mTmpSize);
+ w.calculateSurfaceBounds(attrs, mTmpSize);
+
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -474,9 +466,6 @@
+ " format=" + attrs.format + " flags=" + flags);
}
- // We may abort, so initialize to defaults.
- mLastClipRect.set(0, 0, 0, 0);
-
// Set up surface control with initial size.
try {
@@ -538,40 +527,6 @@
return mSurfaceController;
}
- private void calculateSurfaceBounds(WindowState w, LayoutParams attrs, Rect outSize) {
- outSize.setEmpty();
- if ((attrs.flags & FLAG_SCALED) != 0) {
- // For a scaled surface, we always want the requested size.
- outSize.right = w.mRequestedWidth;
- outSize.bottom = w.mRequestedHeight;
- } else {
- // When we're doing a drag-resizing, request a surface that's fullscreen size,
- // so that we don't need to reallocate during the process. This also prevents
- // buffer drops due to size mismatch.
- if (w.isDragResizing()) {
- final DisplayInfo displayInfo = w.getDisplayInfo();
- outSize.right = displayInfo.logicalWidth;
- outSize.bottom = displayInfo.logicalHeight;
- } else {
- w.getCompatFrameSize(outSize);
- }
- }
-
- // Something is wrong and SurfaceFlinger will not like this, try to revert to reasonable
- // values. This doesn't necessarily mean that there is an error in the system. The sizes
- // might be incorrect, because it is before the first layout or draw.
- if (outSize.width() < 1) {
- outSize.right = 1;
- }
- if (outSize.height() < 1) {
- outSize.bottom = 1;
- }
-
- // Adjust for surface insets.
- outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
- -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
- }
-
boolean hasSurface() {
return mSurfaceController != null && mSurfaceController.hasSurface();
}
@@ -680,77 +635,6 @@
mDsDy = mWin.mGlobalScale;
}
- /**
- * Calculate the window-space crop rect and fill clipRect.
- * @return true if clipRect has been filled otherwise, no window space crop should be applied.
- */
- private boolean calculateCrop(Rect clipRect) {
- final WindowState w = mWin;
- final DisplayContent displayContent = w.getDisplayContent();
- clipRect.setEmpty();
-
- if (displayContent == null) {
- return false;
- }
-
- if (w.getWindowConfiguration().tasksAreFloating()
- || WindowConfiguration.isSplitScreenWindowingMode(w.getWindowingMode())) {
- return false;
- }
-
- // During forced seamless rotation, the surface bounds get updated with the crop in the
- // new rotation, which is not compatible with showing the surface in the old rotation.
- // To work around that we disable cropping for such windows, as it is not necessary anyways.
- if (w.mForceSeamlesslyRotate) {
- return false;
- }
-
- // If we're animating, the wallpaper should only
- // be updated at the end of the animation.
- if (w.mAttrs.type == TYPE_WALLPAPER) {
- return false;
- }
-
- if (DEBUG_WINDOW_CROP) Slog.d(TAG,
- "Updating crop win=" + w + " mLastCrop=" + mLastClipRect);
-
- w.calculatePolicyCrop(mSystemDecorRect);
-
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
- + w.getDecorFrame() + " mSystemDecorRect=" + mSystemDecorRect);
-
- // We use the clip rect as provided by the tranformation for non-fullscreen windows to
- // avoid premature clipping with the system decor rect.
- clipRect.set(mSystemDecorRect);
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect);
-
- w.expandForSurfaceInsets(clipRect);
-
- // The clip rect was generated assuming (0,0) as the window origin,
- // so we need to translate to match the actual surface coordinates.
- clipRect.offset(w.mAttrs.surfaceInsets.left, w.mAttrs.surfaceInsets.top);
-
- if (DEBUG_WINDOW_CROP) Slog.d(TAG,
- "win=" + w + " Clip rect after stack adjustment=" + clipRect);
-
- w.transformClipRectFromScreenToSurfaceSpace(clipRect);
-
- return true;
- }
-
- private void applyCrop(Rect clipRect, boolean recoveringMemory) {
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "applyCrop: win=" + mWin
- + " clipRect=" + clipRect);
- if (clipRect != null) {
- if (!clipRect.equals(mLastClipRect)) {
- mLastClipRect.set(clipRect);
- mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
- }
- } else {
- mSurfaceController.clearCropInTransaction(recoveringMemory);
- }
- }
-
private boolean shouldConsumeMainWindowSizeTransaction() {
// We only consume the transaction when the client is calling relayout
// because this is the only time we know the frameNumber will be valid
@@ -777,31 +661,6 @@
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
- calculateSurfaceBounds(w, attrs, mTmpSize);
-
- // Once relayout has been called at least once, we need to make sure
- // we only resize the client surface during calls to relayout. For
- // clients which use indeterminate measure specs (MATCH_PARENT),
- // we may try and change their window size without a call to relayout.
- // However, this would be unsafe, as the client may be in the middle
- // of producing a frame at the old size, having just completed layout
- // to find the surface size changed underneath it.
- final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
- if (relayout) {
- mSurfaceResized = mSurfaceController.setBufferSizeInTransaction(
- mTmpSize.width(), mTmpSize.height(), recoveringMemory);
- } else {
- mSurfaceResized = false;
- }
- // If we are undergoing seamless rotation, the surface has already
- // been set up to persist at it's old location. We need to freeze
- // updates until a resize occurs.
-
- Rect clipRect = null;
- if (calculateCrop(mTmpClipRect)) {
- clipRect = mTmpClipRect;
- }
-
if (shouldConsumeMainWindowSizeTransaction()) {
task.getMainWindowSizeChangeTask().getSurfaceControl().deferTransactionUntil(
mWin.getClientViewRootSurface(), mWin.getFrameNumber());
@@ -816,6 +675,11 @@
final Rect insets = attrs.surfaceInsets;
+ // getFrameNumber is only valid in the call-stack of relayoutWindow
+ // as this is the only-time we know the client renderer
+ // is paused.
+ final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
+
if (!w.mSeamlesslyRotated) {
// Used to offset the WSA when stack position changes before a resize.
int xOffset = mXOffset;
@@ -838,12 +702,6 @@
}
xOffset = -mTmpPos.x;
yOffset = -mTmpPos.y;
- // Crop also needs to be extended so the bottom isn't cut off when the WSA
- // position is moved.
- if (clipRect != null) {
- clipRect.right += mTmpPos.x;
- clipRect.bottom += mTmpPos.y;
- }
}
}
if (!mIsWallpaper) {
@@ -858,7 +716,6 @@
// Wallpaper is already updated above when calling setWallpaperPositionAndScale so
// we only need to consider the non-wallpaper case here.
if (!mIsWallpaper) {
- applyCrop(clipRect, recoveringMemory);
mSurfaceController.setMatrixInTransaction(
mDsDx * w.mHScale,
mDtDx * w.mVScale,
@@ -866,10 +723,6 @@
mDsDy * w.mVScale, recoveringMemory);
}
}
-
- if (mSurfaceResized) {
- mWin.getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- }
}
/**
@@ -1064,7 +917,6 @@
mDtDy * mWin.mTmpMatrixArray[MSKEW_X] * mWin.mHScale,
mDsDy * mWin.mTmpMatrixArray[MSCALE_Y] * mWin.mVScale,
recoveringMemory);
- applyCrop(null, recoveringMemory);
}
/**
@@ -1255,7 +1107,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- mLastClipRect.dumpDebug(proto, LAST_CLIP_RECT);
if (mSurfaceController != null) {
mSurfaceController.dumpDebug(proto, SURFACE);
}
@@ -1276,11 +1127,7 @@
pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
pw.print(prefix); pw.print("mEnterAnimationPending=" + mEnterAnimationPending);
pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
- pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
- if (!mLastFinalClipRect.isEmpty()) {
- pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
- }
pw.println();
}
@@ -1288,8 +1135,7 @@
pw.print(prefix); pw.print("mPendingDestroySurface=");
pw.println(mPendingDestroySurface);
}
- if (mSurfaceResized || mSurfaceDestroyDeferred) {
- pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
+ if (mSurfaceDestroyDeferred) {
pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
}
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e8bf468..bbcb312 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1468,7 +1468,7 @@
// Need a shared pointer: will be passing it into all unpacking jobs.
std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); });
void* cookie = nullptr;
- const auto libFilePrefix = path::join(constants().libDir, abi);
+ const auto libFilePrefix = path::join(constants().libDir, abi) + "/";
if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
return false;
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 6e3bde3..8b1e9c5 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -94,12 +94,29 @@
* @throws SecurityException if the caller is not system or root
*/
private static void enforceSystemOrRoot(String message) {
- int uid = Binder.getCallingUid();
- if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) {
+ if (!isSystemOrRoot()) {
throw new SecurityException("Only system may " + message);
}
}
+ private static boolean isSystemOrRoot() {
+ final int uid = Binder.getCallingUid();
+ return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || uid == Process.ROOT_UID;
+ }
+
+
+ /**
+ * Enforces that only the system, root UID or SystemUI can make certain calls.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ */
+ private static void enforceSystemRootOrSystemUI(Context context, String message) {
+ if (isSystemOrRoot()) return;
+ context.enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ message);
+ }
+
private final class BinderService extends IPeopleManager.Stub {
@Override
@@ -126,7 +143,7 @@
@Override
public long getLastInteraction(String packageName, int userId, String shortcutId) {
- enforceSystemOrRoot("get last interaction");
+ enforceSystemRootOrSystemUI(getContext(), "get last interaction");
return mDataManager.getLastInteraction(packageName, userId, shortcutId);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 5d8f662..a250c21 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -351,7 +351,7 @@
doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
- doAnswer(answer((minTaskLayer, callback) -> {
+ doAnswer(answer(callback -> {
Field field = callback.getClass().getDeclaredField("adj");
field.set(callback, VISIBLE_APP_ADJ);
field = callback.getClass().getDeclaredField("foregroundActivities");
@@ -361,7 +361,7 @@
field = callback.getClass().getDeclaredField("schedGroup");
field.set(callback, SCHED_GROUP_TOP_APP);
return 0;
- })).when(wpc).computeOomAdjFromActivities(anyInt(),
+ })).when(wpc).computeOomAdjFromActivities(
any(WindowProcessController.ComputeOomAdjCallback.class));
sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 13f04d2..2efd4b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -117,7 +117,6 @@
assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex);
assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId);
assertEquals(mMockLeash, app.leash);
- assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50);
@@ -274,7 +273,6 @@
assertEquals(new Rect(0, 0, 200, 200), app.startBounds);
assertEquals(mMockLeash, app.leash);
assertEquals(mMockThumbnailLeash, app.startLeash);
- assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
@@ -325,7 +323,6 @@
assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
assertEquals(mMockLeash, app.leash);
assertEquals(mMockThumbnailLeash, app.startLeash);
- assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3053fe6..cc8b2a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -173,6 +173,35 @@
}
@Test
+ public void testTaskLayerRank() {
+ final Task rootTask = new TaskBuilder(mSupervisor).build();
+ final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+ new ActivityBuilder(mAtm).setStack(task1).build().mVisibleRequested = true;
+ // RootWindowContainer#invalidateTaskLayers should post to update.
+ waitHandlerIdle(mWm.mH);
+
+ assertEquals(1, task1.mLayerRank);
+ // Only tasks that directly contain activities have a ranking.
+ assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank);
+
+ final Task task2 = new TaskBuilder(mSupervisor).build();
+ new ActivityBuilder(mAtm).setStack(task2).build().mVisibleRequested = true;
+ waitHandlerIdle(mWm.mH);
+
+ // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
+ // activities have the visible rank.
+ assertEquals(2, task1.mLayerRank);
+ // The task2 is the top task, so it has a lower rank as a higher priority oom score.
+ assertEquals(1, task2.mLayerRank);
+
+ task2.moveToBack("test", null /* task */);
+ waitHandlerIdle(mWm.mH);
+
+ assertEquals(1, task1.mLayerRank);
+ assertEquals(2, task2.mLayerRank);
+ }
+
+ @Test
public void testForceStopPackage() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = task.getTopMostActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index ca3626d..0cf63f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -115,12 +115,6 @@
expectedRect.bottom);
}
- private void assertPolicyCrop(WindowState w, int left, int top, int right, int bottom) {
- Rect policyCrop = new Rect();
- w.calculatePolicyCrop(policyCrop);
- assertRect(policyCrop, left, top, right, bottom);
- }
-
@Test
public void testLayoutInFullscreenTaskInsets() {
// fullscreen task doesn't use bounds for computeFrame
@@ -335,12 +329,10 @@
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, df, cf, vf, dcf, sf);
w.computeFrame();
- assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
w.computeFrame();
- assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
// Now we set up a window which doesn't fill the entire decor frame.
// Normally it would be cropped to it's frame but in the case of docked resizing
@@ -355,16 +347,7 @@
w.mRequestedHeight = logicalHeight / 2;
w.computeFrame();
- // Normally the crop is shrunk from the decor frame
- // to the computed window frame.
- assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
-
doReturn(true).when(w).isDockedResizing();
- // But if we are docked resizing it won't be, however we will still be
- // shrunk to the decor frame and the display.
- assertPolicyCrop(w, 0, 0,
- Math.min(pf.width(), displayInfo.logicalWidth),
- Math.min(pf.height(), displayInfo.logicalHeight));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 38c7531..e50c009 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -277,6 +277,69 @@
mWpc.getConfiguration().seq, globalSeq);
}
+ @Test
+ public void testComputeOomAdjFromActivities() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setUseProcess(mWpc)
+ .build();
+ activity.mVisibleRequested = true;
+ final int[] callbackResult = { 0 };
+ final int visible = 1;
+ final int paused = 2;
+ final int stopping = 4;
+ final int other = 8;
+ final WindowProcessController.ComputeOomAdjCallback callback =
+ new WindowProcessController.ComputeOomAdjCallback() {
+ @Override
+ public void onVisibleActivity() {
+ callbackResult[0] |= visible;
+ }
+
+ @Override
+ public void onPausedActivity() {
+ callbackResult[0] |= paused;
+ }
+
+ @Override
+ public void onStoppingActivity(boolean finishing) {
+ callbackResult[0] |= stopping;
+ }
+
+ @Override
+ public void onOtherActivity() {
+ callbackResult[0] |= other;
+ }
+ };
+
+ // onStartActivity should refresh the state immediately.
+ mWpc.onStartActivity(0 /* topProcessState */, activity.info);
+ assertEquals(1 /* minTaskLayer */, mWpc.computeOomAdjFromActivities(callback));
+ assertEquals(visible, callbackResult[0]);
+
+ // The oom state will be updated in handler from activity state change.
+ callbackResult[0] = 0;
+ activity.mVisibleRequested = false;
+ activity.setState(Task.ActivityState.PAUSED, "test");
+ waitHandlerIdle(mAtm.mH);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(paused, callbackResult[0]);
+
+ // updateProcessInfo with updateOomAdj=true should refresh the state immediately.
+ callbackResult[0] = 0;
+ activity.setState(Task.ActivityState.STOPPING, "test");
+ mWpc.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, true /* updateOomAdj */, false /* addPendingTopUid */);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(stopping, callbackResult[0]);
+
+ callbackResult[0] = 0;
+ activity.setState(Task.ActivityState.STOPPED, "test");
+ waitHandlerIdle(mAtm.mH);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(other, callbackResult[0]);
+ }
+
private TestDisplayContent createTestDisplayContentInContainer() {
return new TestDisplayContent.Builder(mAtm, 1000, 1500).build();
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5b3da61..8261b53 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -79,6 +79,30 @@
*/
public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
+ /**
+ * Only send USSD over IMS while CS is out of service, otherwise send USSD over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_CS_PREFERRED = 0;
+
+ /**
+ * Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_IMS_PREFERRED = 1;
+
+ /**
+ * Only send USSD over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_CS_ONLY = 2;
+
+ /**
+ * Only send USSD over IMS and disallow silent redial over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_IMS_ONLY = 3;
+
private final Context mContext;
/**
@@ -584,6 +608,20 @@
public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
/**
+ * Specify the method of selection for UE sending USSD requests. The default value is
+ * {@link #USSD_OVER_CS_PREFERRED}.
+ * <p> Available options:
+ * <ul>
+ * <li>0: {@link #USSD_OVER_CS_PREFERRED} </li>
+ * <li>1: {@link #USSD_OVER_IMS_PREFERRED} </li>
+ * <li>2: {@link #USSD_OVER_CS_ONLY} </li>
+ * <li>3: {@link #USSD_OVER_IMS_ONLY} </li>
+ * </ul>
+ */
+ public static final String KEY_CARRIER_USSD_METHOD_INT =
+ "carrier_ussd_method_int";
+
+ /**
* Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
* By default this value is {@code false}.
*
@@ -3963,6 +4001,7 @@
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+ sDefaults.putInt(KEY_CARRIER_USSD_METHOD_INT, USSD_OVER_CS_PREFERRED);
sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false);
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a82d988..7a0e1cd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -85,8 +85,6 @@
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -94,7 +92,6 @@
import android.util.Log;
import android.util.Pair;
-import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CellNetworkScanResult;
@@ -2807,7 +2804,11 @@
/** Current network is LTE_CA {@hide} */
@UnsupportedAppUsage
public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
- /** Current network is NR(New Radio) 5G. */
+ /**
+ * Current network is NR (New Radio) 5G.
+ * This will only be returned for 5G SA.
+ * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}.
+ */
public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
private static final @NetworkType int[] NETWORK_TYPES = {
@@ -7380,80 +7381,6 @@
}
/**
- * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
- * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
- * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
- * @hide
- */
- public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getMmTelFeatureAndListen(slotIndex, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
- + e.getMessage());
- }
- return null;
- }
-
- /**
- * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS
- * feature for emergency calling or {@link null} if the service is not available. If an
- * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
- * listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
- * @hide
- */
- public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getRcsFeatureAndListen(slotIndex, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: "
- + e.getMessage());
- }
- return null;
- }
-
- /**
- * Unregister a IImsServiceFeatureCallback previously associated with an ImsFeature through
- * {@link #getImsMmTelFeatureAndListen(int, IImsServiceFeatureCallback)} or
- * {@link #getImsRcsFeatureAndListen(int, IImsServiceFeatureCallback)}.
- * @param slotIndex The SIM slot associated with the callback.
- * @param featureType The {@link android.telephony.ims.feature.ImsFeature.FeatureType}
- * associated with the callback.
- * @param callback The callback to be unregistered.
- * @hide
- */
- public void unregisterImsFeatureCallback(int slotIndex, int featureType,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.unregisterImsFeatureCallback(slotIndex, featureType, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "unregisterImsFeatureCallback, RemoteException: "
- + e.getMessage());
- }
- }
-
- /**
* @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
* @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
* @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index f6c14e6..ee2fce7 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -59,6 +59,7 @@
* manager.
*/
public class ImsMmTelManager implements RegistrationManager {
+ private static final String TAG = "ImsMmTelManager";
/**
* @hide
@@ -809,7 +810,7 @@
}
try {
- getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+ iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
executor.execute(() -> callback.accept(result == 1));
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index da7311c..8a05bdf 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,6 +16,7 @@
package android.telephony.ims;
+import android.annotation.LongDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
@@ -41,6 +42,11 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
* ImsService must register the service in their AndroidManifest to be detected by the framework.
@@ -98,6 +104,32 @@
private static final String LOG_TAG = "ImsService";
/**
+ * This ImsService supports the capability to place emergency calls over MMTEL.
+ * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
+ * adding other capabilities in a central location, so track this capability here as well.
+ */
+ public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
+
+ /**
+ * @hide
+ */
+ @LongDef(flag = true,
+ prefix = "CAPABILITY_",
+ value = {
+ CAPABILITY_EMERGENCY_OVER_MMTEL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsServiceCapability {}
+
+ /**
+ * Used for logging purposes, see {@link #getCapabilitiesString(long)}
+ * @hide
+ */
+ private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
+ put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+ }};
+
+ /**
* The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
* @hide
*/
@@ -409,4 +441,30 @@
public ImsRegistrationImplBase getRegistration(int slotId) {
return new ImsRegistrationImplBase();
}
+
+ /**
+ * @return A string representation of the ImsService capabilties for logging.
+ * @hide
+ */
+ public static String getCapabilitiesString(@ImsServiceCapability long caps) {
+ StringBuffer result = new StringBuffer();
+ result.append("capabilities={ ");
+ // filter incrementally fills 0s from left to right. This is used to keep filtering out
+ // more bits in the long until the remaining leftmost bits are all zero.
+ long filter = 0xFFFFFFFFFFFFFFFFL;
+ // position of iterator to potentially print capability.
+ long i = 0;
+ while ((caps & filter) != 0 && i <= 63) {
+ long bitToCheck = (1L << i);
+ if ((caps & bitToCheck) != 0) {
+ result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
+ result.append(" ");
+ }
+ // shift left by one and fill in another 1 on the leftmost bit.
+ filter <<= 1;
+ i++;
+ }
+ result.append("}");
+ return result.toString();
+ }
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 9e46142..d012703b 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -22,6 +22,7 @@
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.IIntegerConsumer;
/**
@@ -50,4 +51,9 @@
void setUceSettingEnabled(int subId, boolean isEnabled);
void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
+
+ // Internal commands that should not be made public
+ void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback,
+ boolean oneShot);
+ void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
}
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.aidl b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
new file mode 100644
index 0000000..9706f20
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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.ims;
+
+parcelable ImsFeatureContainer;
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
new file mode 100644
index 0000000..b259679
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 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.ims;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.ImsFeature;
+
+import java.util.Objects;
+
+/**
+ * Contains an IBinder linking to the appropriate ImsFeature as well as the associated
+ * interfaces.
+ * @hide
+ */
+public final class ImsFeatureContainer implements Parcelable {
+ /**
+ * ImsFeature that is being tracked.
+ */
+ public final IBinder imsFeature;
+
+ /**
+ * IImsConfig interface that should be associated with the ImsFeature.
+ */
+ public final android.telephony.ims.aidl.IImsConfig imsConfig;
+
+ /**
+ * IImsRegistration interface that should be associated with this ImsFeature.
+ */
+ public final IImsRegistration imsRegistration;
+
+ /**
+ * State of the feature that is being tracked.
+ */
+ private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
+
+ /**
+ * Capabilities of this ImsService.
+ */
+ private @ImsService.ImsServiceCapability long mCapabilities;
+ /**
+ * Contains the ImsFeature IBinder as well as the ImsService interfaces associated with
+ * that feature.
+ * @param iFace IBinder connection to the ImsFeature.
+ * @param iConfig IImsConfig interface associated with the ImsFeature.
+ * @param iReg IImsRegistration interface associated with the ImsFeature
+ * @param initialCaps The initial capabilities that the ImsService supports.
+ */
+ public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
+ @NonNull IImsRegistration iReg, long initialCaps) {
+ imsFeature = iFace;
+ imsConfig = iConfig;
+ imsRegistration = iReg;
+ mCapabilities = initialCaps;
+ }
+
+ /**
+ * Create an ImsFeatureContainer from a Parcel.
+ */
+ private ImsFeatureContainer(Parcel in) {
+ imsFeature = in.readStrongBinder();
+ imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
+ imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+ mState = in.readInt();
+ mCapabilities = in.readLong();
+ }
+
+ /**
+ * @return the capabilties that are associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public @ImsService.ImsServiceCapability long getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * Update the capabilities that are associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public void setCapabilities(@ImsService.ImsServiceCapability long caps) {
+ mCapabilities = caps;
+ }
+
+ /**
+ * @return The state of the ImsFeature.
+ */
+ public @ImsFeature.ImsState int getState() {
+ return mState;
+ }
+
+ /**
+ * Set the state that is associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public void setState(@ImsFeature.ImsState int state) {
+ mState = state;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ImsFeatureContainer that = (ImsFeatureContainer) o;
+ return imsFeature.equals(that.imsFeature) &&
+ imsConfig.equals(that.imsConfig) &&
+ imsRegistration.equals(that.imsRegistration) &&
+ mState == that.getState() &&
+ mCapabilities == that.getCapabilities();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+ }
+
+ @Override
+ public String toString() {
+ return "FeatureContainer{" +
+ "imsFeature=" + imsFeature +
+ ", imsConfig=" + imsConfig +
+ ", imsRegistration=" + imsRegistration +
+ ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
+ ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(imsFeature);
+ dest.writeStrongInterface(imsConfig);
+ dest.writeStrongInterface(imsRegistration);
+ dest.writeInt(mState);
+ dest.writeLong(mCapabilities);
+ }
+
+
+ public static final Creator<ImsFeatureContainer> CREATOR = new Creator<ImsFeatureContainer>() {
+ @Override
+ public ImsFeatureContainer createFromParcel(Parcel source) {
+ return new ImsFeatureContainer(source);
+ }
+
+ @Override
+ public ImsFeatureContainer[] newArray(int size) {
+ return new ImsFeatureContainer[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index 9a9cf53..f5f67bd 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -16,13 +16,18 @@
package com.android.ims.internal;
+import com.android.ims.ImsFeatureContainer;
/**
- * Interface from ImsResolver to ImsServiceProxy in ImsManager.
- * Callback to ImsManager when a feature changes in the ImsServiceController.
+ * Interface from ImsResolver to FeatureConnections.
+ * Callback to FeatureConnections when a feature's status changes.
* {@hide}
*/
oneway interface IImsServiceFeatureCallback {
- void imsFeatureCreated(int slotId, int feature);
- void imsFeatureRemoved(int slotId, int feature);
- void imsStatusChanged(int slotId, int feature, int status);
+ void imsFeatureCreated(in ImsFeatureContainer feature);
+ // Reason defined in FeatureConnector.UnavailableReason
+ void imsFeatureRemoved(int reason);
+ // Status defined in ImsFeature.ImsState.
+ void imsStatusChanged(int status);
+ //Capabilities defined in ImsService.ImsServiceCapability
+ void updateCapabilities(long capabilities);
}
\ 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 02a74ba..ef5078d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -829,22 +829,15 @@
* as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
* interface.
*/
- IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
-
- /**
- * Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
- * as well as registering the RcsFeature for callbacks using the IImsServiceFeatureCallback
- * interface.
- */
- IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+ void registerMmTelFeatureCallback(int slotId, in IImsServiceFeatureCallback callback,
+ boolean oneShot);
/**
* Unregister a callback that was previously registered through
- * {@link #getMmTelFeatureAndListen} or {@link #getRcsFeatureAndListen}. This should always be
- * called when the callback is no longer being used.
+ * {@link #registerMmTelFeatureCallback}. This should always be called when the callback is no
+ * longer being used.
*/
- void unregisterImsFeatureCallback(int slotId, int featureType,
- in IImsServiceFeatureCallback callback);
+ void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
/**
* Returns the IImsRegistration associated with the slot and feature specified.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 05a59ef..9a2def9 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -745,7 +745,7 @@
</activity>
<activity android:name="BlurActivity"
- android:label="Shaders/Blur"
+ android:label="RenderEffect/Blur"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/HwAccelerationTest/res/layout/image_filter_activity.xml b/tests/HwAccelerationTest/res/layout/image_filter_activity.xml
new file mode 100644
index 0000000..a0ee67a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/image_filter_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/image_filter_test_view"
+ android:background="#FF0000"
+ android:layout_width="200dp"
+ android:layout_height="200dp" />
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
index 033fb0e..e4ca788 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
@@ -18,11 +18,12 @@
import android.app.Activity;
import android.content.Context;
-import android.graphics.BlurShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
import android.graphics.Shader;
import android.os.Bundle;
import android.view.Gravity;
@@ -51,16 +52,27 @@
}
public static class BlurGradientView extends View {
- private BlurShader mBlurShader = null;
- private Paint mPaint;
+ private final float mBlurRadius = 25f;
+ private final Paint mPaint;
+ private final RenderNode mRenderNode;
public BlurGradientView(Context c) {
super(c);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRenderNode = new RenderNode("BlurGradientView");
+ mRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(
+ mBlurRadius,
+ mBlurRadius,
+ null,
+ Shader.TileMode.DECAL
+ )
+ );
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed || mBlurShader == null) {
+ if (changed) {
LinearGradient gradient = new LinearGradient(
0f,
0f,
@@ -70,41 +82,81 @@
Color.YELLOW,
Shader.TileMode.CLAMP
);
- mBlurShader = new BlurShader(30f, 40f, gradient);
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setShader(mBlurShader);
+
+ mPaint.setShader(gradient);
+
+ final int width = right - left;
+ final int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+
+ Canvas canvas = mRenderNode.beginRecording();
+ canvas.drawRect(
+ mBlurRadius * 2,
+ mBlurRadius * 2,
+ width - mBlurRadius * 2,
+ height - mBlurRadius * 2,
+ mPaint
+ );
+ mRenderNode.endRecording();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+ canvas.drawRenderNode(mRenderNode);
}
}
public static class BlurView extends View {
- private final BlurShader mBlurShader;
private final Paint mPaint;
+ private final RenderNode mRenderNode;
+ private final float mBlurRadius = 20f;
public BlurView(Context c) {
super(c);
- mBlurShader = new BlurShader(20f, 20f, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setShader(mBlurShader);
+ mRenderNode = new RenderNode("blurNode");
+ mRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(
+ mBlurRadius,
+ mBlurRadius,
+ null,
+ Shader.TileMode.DECAL
+ )
+ );
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed) {
+ int width = right - left;
+ int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+ Canvas canvas = mRenderNode.beginRecording(width, height);
+ mPaint.setColor(Color.BLUE);
+
+ canvas.drawRect(
+ mBlurRadius * 2,
+ mBlurRadius * 2,
+ width - mBlurRadius * 2,
+ height - mBlurRadius * 2,
+ mPaint
+ );
+
+ mPaint.setColor(Color.RED);
+ canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+ mRenderNode.endRecording();
+ }
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
- mPaint.setColor(Color.BLUE);
- canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
-
- mPaint.setColor(Color.RED);
- canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, 50f, mPaint);
+ canvas.drawRenderNode(mRenderNode);
}
}
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 71175847..2f2578b 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -20,13 +20,13 @@
import android.graphics.BitmapFactory
import android.graphics.BitmapShader
import android.graphics.BlendMode
-import android.graphics.BlurShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
-import android.graphics.RadialGradient
import android.graphics.Rect
+import android.graphics.RenderEffect
+import android.graphics.RenderNode
import android.graphics.Shader
import android.hardware.Sensor
import android.hardware.SensorEvent
@@ -36,7 +36,6 @@
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
-import com.android.internal.graphics.ColorUtils
import com.android.test.silkfx.R
import kotlin.math.sin
import kotlin.math.sqrt
@@ -152,10 +151,19 @@
var blurRadius = 150f
set(value) {
field = value
- blurPaint.shader = BlurShader(value, value, null)
+ renderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(value, value, Shader.TileMode.CLAMP))
invalidate()
}
+ private var renderNodeIsDirty = true
+ private val renderNode = RenderNode("GlassRenderNode")
+
+ override fun invalidate() {
+ renderNodeIsDirty = true
+ super.invalidate()
+ }
+
init {
setWillNotDraw(false)
materialPaint.blendMode = BlendMode.SOFT_LIGHT
@@ -164,7 +172,6 @@
scrimPaint.alpha = (scrimOpacity * 255).toInt()
noisePaint.alpha = (noiseOpacity * 255).toInt()
materialPaint.alpha = (materialOpacity * 255).toInt()
- blurPaint.shader = BlurShader(blurRadius, blurRadius, null)
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) {
outline?.setRoundRect(Rect(0, 0, width, height), 100f)
@@ -184,20 +191,8 @@
}
override fun onDraw(canvas: Canvas?) {
- src.set(-width / 2, -height / 2, width / 2, height / 2)
- src.scale(1.0f + zoom)
- val centerX = left + width / 2
- val centerY = top + height / 2
- val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
- val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
- src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
- src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
-
- dst.set(0, 0, width, height)
- canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
- canvas?.drawRect(dst, materialPaint)
- canvas?.drawRect(dst, noisePaint)
- canvas?.drawRect(dst, scrimPaint)
+ updateGlassRenderNode()
+ canvas?.drawRenderNode(renderNode)
}
fun resetGyroOffsets() {
@@ -205,4 +200,31 @@
gyroYRotation = 0f
invalidate()
}
+
+ private fun updateGlassRenderNode() {
+ if (renderNodeIsDirty) {
+ renderNode.setPosition(0, 0, getWidth(), getHeight())
+
+ val canvas = renderNode.beginRecording()
+
+ src.set(-width / 2, -height / 2, width / 2, height / 2)
+ src.scale(1.0f + zoom)
+ val centerX = left + width / 2
+ val centerY = top + height / 2
+ val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
+ val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
+ src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
+ src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
+
+ dst.set(0, 0, width, height)
+ canvas.drawBitmap(backgroundBitmap, src, dst, blurPaint)
+ canvas.drawRect(dst, materialPaint)
+ canvas.drawRect(dst, noisePaint)
+ canvas.drawRect(dst, scrimPaint)
+
+ renderNode.endRecording()
+
+ renderNodeIsDirty = false
+ }
+ }
}
\ No newline at end of file
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index fd45ebe..0f71ab4 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -509,7 +509,7 @@
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
- field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
+ field @Deprecated public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
@@ -517,7 +517,7 @@
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
- field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field @Deprecated public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2219bfc..b4e4210 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -931,9 +931,6 @@
* This can be as a result of adding/updating/deleting a network.
* <br />
* {@link #EXTRA_CHANGE_REASON} contains whether the configuration was added/changed/removed.
- * {@link #EXTRA_WIFI_CONFIGURATION} is never set starting in Android 11.
- * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set for backwards compatibility reasons, but
- * its value is always true, even if only a single network changed.
* <br />
* The {@link android.Manifest.permission#ACCESS_WIFI_STATE ACCESS_WIFI_STATE} permission is
* required to receive this broadcast.
@@ -947,17 +944,20 @@
* The lookup key for a {@link android.net.wifi.WifiConfiguration} object representing
* the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION}
* broadcast is sent.
- * Note: this extra is never set starting in Android 11.
+ * @deprecated this extra is never set. Use {@link #getConfiguredNetworks} to get the full list
+ * of configured networks.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
/**
* Multiple network configurations have changed.
* @see #CONFIGURED_NETWORKS_CHANGED_ACTION
- * Note: this extra is always true starting in Android 11.
+ * @deprecated this extra's value is always true.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
/**