Merge "Add Context Hub Service OWNERS to context_hub_info.proto" into main
diff --git a/api/Android.bp b/api/Android.bp
index 27c372a..a148cbd 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -200,7 +200,7 @@
out: ["current.srcjar"],
cmd: "$(location merge_zips) $(out) $(in)",
srcs: [
- ":api-stubs-docs-non-updatable",
+ ":api-stubs-docs-non-updatable{.exportable}",
":all-modules-public-stubs-source",
],
visibility: ["//visibility:private"], // Used by make module in //development, mind
diff --git a/core/api/current.txt b/core/api/current.txt
index 2e3fe5b..08c23dd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16525,8 +16525,8 @@
field public static final int START_HYPHEN_EDIT_NO_EDIT = 0; // 0x0
field public static final int STRIKE_THRU_TEXT_FLAG = 16; // 0x10
field public static final int SUBPIXEL_TEXT_FLAG = 128; // 0x80
- field @FlaggedApi("com.android.text.flags.inter_character_justification") public static final int TEXT_RUN_FLAG_LEFT_EDGE = 8192; // 0x2000
- field @FlaggedApi("com.android.text.flags.inter_character_justification") public static final int TEXT_RUN_FLAG_RIGHT_EDGE = 16384; // 0x4000
+ field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int TEXT_RUN_FLAG_LEFT_EDGE = 8192; // 0x2000
+ field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int TEXT_RUN_FLAG_RIGHT_EDGE = 16384; // 0x4000
field public static final int UNDERLINE_TEXT_FLAG = 8; // 0x8
}
@@ -18003,7 +18003,7 @@
field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2
field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0
field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1
- field @FlaggedApi("com.android.text.flags.inter_character_justification") public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2; // 0x2
+ field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2; // 0x2
field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1
field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0
}
@@ -47278,7 +47278,7 @@
method public int getLineForOffset(int);
method public int getLineForVertical(int);
method public float getLineLeft(int);
- method @FlaggedApi("com.android.text.flags.inter_character_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean);
+ method @FlaggedApi("com.android.text.flags.letter_spacing_justification") @IntRange(from=0) public int getLineLetterSpacingUnitCount(@IntRange(from=0) int, boolean);
method public float getLineMax(int);
method public float getLineRight(int);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public final float getLineSpacingAmount();
@@ -47329,7 +47329,7 @@
field @NonNull public static final android.text.Layout.TextInclusionStrategy INCLUSION_STRATEGY_ANY_OVERLAP;
field @NonNull public static final android.text.Layout.TextInclusionStrategy INCLUSION_STRATEGY_CONTAINS_ALL;
field @NonNull public static final android.text.Layout.TextInclusionStrategy INCLUSION_STRATEGY_CONTAINS_CENTER;
- field @FlaggedApi("com.android.text.flags.inter_character_justification") public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2; // 0x2
+ field @FlaggedApi("com.android.text.flags.letter_spacing_justification") public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2; // 0x2
field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1
field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0dbce97..5074ed7 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4588,19 +4588,19 @@
*/
private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
@UidState int toUidState, @OpFlags int flags) {
- NoteOpEvent lastAccessEvent = null;
+ NoteOpEvent lastRejectEvent = null;
for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
- NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastRejectEvent(
+ NoteOpEvent lastAttributionRejectEvent = attributionEntry.getLastRejectEvent(
fromUidState, toUidState, flags);
- if (lastAccessEvent == null || (lastAttributionAccessEvent != null
- && lastAttributionAccessEvent.getNoteTime()
- > lastAccessEvent.getNoteTime())) {
- lastAccessEvent = lastAttributionAccessEvent;
+ if (lastRejectEvent == null || (lastAttributionRejectEvent != null
+ && lastAttributionRejectEvent.getNoteTime()
+ > lastRejectEvent.getNoteTime())) {
+ lastRejectEvent = lastAttributionRejectEvent;
}
}
- return lastAccessEvent;
+ return lastRejectEvent;
}
/**
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 1ea80f1..8ddb42d 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -18,7 +18,7 @@
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
-import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;
+import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
@@ -172,7 +172,7 @@
/**
* Value for justification mode indicating the text is justified by stretching letter spacing.
*/
- @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
public static final int JUSTIFICATION_MODE_INTER_CHARACTER =
LineBreaker.JUSTIFICATION_MODE_INTER_CHARACTER;
@@ -1831,7 +1831,7 @@
* @return the number of cluster count in the line.
*/
@IntRange(from = 0)
- @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
public int getLineLetterSpacingUnitCount(@IntRange(from = 0) int line,
boolean includeTrailingWhitespace) {
final int start = getLineStart(line);
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 224e5d8..bde9c77 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -305,7 +305,7 @@
}
mAddedWordSpacingInPx = (justifyWidth - width) / spaces;
mAddedLetterSpacingInPx = 0;
- } else { // justificationMode == Layout.JUSTIFICATION_MODE_INTER_CHARACTER
+ } else { // justificationMode == Layout.JUSTIFICATION_MODE_LETTER_SPACING
LineInfo lineInfo = new LineInfo();
float width = Math.abs(measure(end, false, null, null, lineInfo));
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index a49aee1..f37c4c2a 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -77,7 +77,7 @@
}
flag {
- name: "inter_character_justification"
+ name: "letter_spacing_justification"
namespace: "text"
description: "A feature flag that implement inter character justification."
bug: "283193133"
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index ddedca2..50ff7c9 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -79,16 +79,16 @@
android:layout_height="1dp"
style="@style/AutofillHalfSheetDivider" />
- <com.android.internal.widget.ButtonBarLayout
+ <com.android.server.autofill.ui.BottomSheetButtonBarLayout
+ android:id="@+id/autofill_save_button_bar"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="wrap_content"
android:layout_gravity="end"
android:clipToPadding="false"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:theme="@style/Theme.DeviceDefault.AutofillHalfScreenDialogButton"
android:orientation="horizontal"
- android:gravity="center_vertical"
android:layout_marginStart="@dimen/autofill_save_outer_margin"
android:layout_marginEnd="@dimen/autofill_save_outer_margin"
>
@@ -100,12 +100,15 @@
android:paddingEnd="12dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:minWidth="0dp"
style="?android:attr/borderlessButtonStyle"
android:text="@string/autofill_save_no">
</Button>
<Space
+ android:id="@+id/autofill_button_bar_spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
@@ -116,12 +119,14 @@
android:id="@+id/autofill_save_yes"
android:layout_width="wrap_content"
android:layout_height="40dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:minWidth="0dp"
style="@style/AutofillHalfSheetTonalButton"
android:text="@string/autofill_save_yes">
</Button>
- </com.android.internal.widget.ButtonBarLayout>
+ </com.android.server.autofill.ui.BottomSheetButtonBarLayout>
</com.android.server.autofill.ui.BottomSheetLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 96c4bf4..0acccee 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -887,6 +887,8 @@
<dimen name="autofill_save_scroll_view_top_margin">16dp</dimen>
<dimen name="autofill_save_button_bar_padding">16dp</dimen>
<dimen name="autofill_dialog_corner_radius">24dp</dimen>
+ <dimen name="autofill_button_bar_spacer_width">12dp</dimen>
+ <dimen name="autofill_button_bar_spacer_height">4dp</dimen>
<!-- How much extra space should be left around the autofill dialog -->
<dimen name="autofill_dialog_offset">72dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4cf37df..58427b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3728,6 +3728,7 @@
<java-symbol type="id" name="autofill_save_no" />
<java-symbol type="id" name="autofill_save_title" />
<java-symbol type="id" name="autofill_save_yes" />
+ <java-symbol type="id" name="autofill_button_bar_spacer" />
<java-symbol type="id" name="autofill_service_icon" />
<java-symbol type="id" name="autofill_dialog_picker"/>
<java-symbol type="id" name="autofill_dialog_header"/>
@@ -3774,6 +3775,8 @@
<java-symbol type="dimen" name="autofill_dialog_max_width" />
<java-symbol type="dimen" name="autofill_dialog_offset"/>
<java-symbol type="dimen" name="autofill_save_outer_margin"/>
+ <java-symbol type="dimen" name="autofill_button_bar_spacer_width"/>
+ <java-symbol type="dimen" name="autofill_button_bar_spacer_height"/>
<java-symbol type="bool" name="autofill_dialog_horizontal_space_included"/>
diff --git a/core/tests/coretests/src/android/text/TextLineJustificationTest.kt b/core/tests/coretests/src/android/text/TextLineJustificationTest.kt
index a525615..c18f35f 100644
--- a/core/tests/coretests/src/android/text/TextLineJustificationTest.kt
+++ b/core/tests/coretests/src/android/text/TextLineJustificationTest.kt
@@ -126,4 +126,4 @@
TextLine.recycle(tl)
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt b/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt
index 27869bb..71980c1 100644
--- a/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt
+++ b/core/tests/coretests/src/android/text/TextLineLetterSpacingTest.kt
@@ -21,7 +21,7 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION
+import com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -40,7 +40,7 @@
@JvmField
val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
- @RequiresFlagsEnabled(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @RequiresFlagsEnabled(FLAG_LETTER_SPACING_JUSTIFICATION)
@Test
fun calculateRunFlagTest() {
// Only one Bidi run
@@ -84,7 +84,7 @@
.isEqualTo(LEFT_EDGE)
}
- @RequiresFlagsEnabled(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @RequiresFlagsEnabled(FLAG_LETTER_SPACING_JUSTIFICATION)
@Test
fun resolveRunFlagForSubSequenceTest() {
val runStart = 5
@@ -221,4 +221,4 @@
MIDDLE_OF_LINE, true, runStart, runEnd, runStart + 1, runEnd - 1))
.isEqualTo(MIDDLE_OF_LINE)
}
-}
\ No newline at end of file
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index ae61a2d..df95a91 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -17,7 +17,7 @@
package android.graphics;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
-import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;
+import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
@@ -293,7 +293,7 @@
* {@link Paint#measureText(CharSequence, int, int)}. The non-run based APIs works as both
* {@link #TEXT_RUN_FLAG_LEFT_EDGE} and {@link #TEXT_RUN_FLAG_RIGHT_EDGE} are specified.
*/
- @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
public static final int TEXT_RUN_FLAG_LEFT_EDGE = 0x2000;
@@ -324,7 +324,7 @@
* {@link Paint#measureText(CharSequence, int, int)}. The non-run based APIs works as both
* {@link #TEXT_RUN_FLAG_LEFT_EDGE} and {@link #TEXT_RUN_FLAG_RIGHT_EDGE} are specified.
*/
- @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
public static final int TEXT_RUN_FLAG_RIGHT_EDGE = 0x4000;
// These flags are always set on a new/reset paint, even if flags 0 is passed.
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 9707126..0e9f29d 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -17,7 +17,7 @@
package android.graphics.text;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
-import static com.android.text.flags.Flags.FLAG_INTER_CHARACTER_JUSTIFICATION;
+import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
@@ -183,7 +183,7 @@
/**
* Value for justification mode indicating the text is justified by stretching letter spacing.
*/
- @FlaggedApi(FLAG_INTER_CHARACTER_JUSTIFICATION)
+ @FlaggedApi(FLAG_LETTER_SPACING_JUSTIFICATION)
public static final int JUSTIFICATION_MODE_INTER_CHARACTER = 2;
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/OWNERS
new file mode 100644
index 0000000..2ff4d90
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/OWNERS
@@ -0,0 +1,4 @@
+# WM shell transition tracing owners
+# Bug component: 1157642
+natanieljr@google.com
+pablogamito@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
index b3e8bd9..fa331af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -196,14 +196,18 @@
mDataSource.trace(ctx -> {
final ProtoOutputStream os = ctx.newTracePacket();
+ final long mappingsToken = os.start(PerfettoTrace.TracePacket.SHELL_HANDLER_MAPPINGS);
for (Map.Entry<String, Integer> entry : mHandlerMapping.entrySet()) {
final String handler = entry.getKey();
final int handlerId = entry.getValue();
- final long token = os.start(PerfettoTrace.TracePacket.SHELL_HANDLER_MAPPINGS);
+
+ final long mappingEntryToken = os.start(PerfettoTrace.ShellHandlerMappings.MAPPING);
os.write(PerfettoTrace.ShellHandlerMapping.ID, handlerId);
os.write(PerfettoTrace.ShellHandlerMapping.NAME, handler);
- os.end(token);
+ os.end(mappingEntryToken);
+
}
+ os.end(mappingsToken);
ctx.flush();
});
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index 6ebfc63..ac75c07 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -41,9 +41,9 @@
#endif // __ANDROID__
}
-inline bool inter_character_justification() {
+inline bool letter_spacing_justification() {
#ifdef __ANDROID__
- return com_android_text_flags_inter_character_justification();
+ return com_android_text_flags_letter_spacing_justification();
#else
return true;
#endif // __ANDROID__
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 5613369..d66d7f8 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -72,7 +72,7 @@
const minikin::Range contextRange(contextStart, contextStart + contextCount);
const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit();
const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
- const minikin::RunFlag minikinRunFlag = text_feature::inter_character_justification()
+ const minikin::RunFlag minikinRunFlag = text_feature::letter_spacing_justification()
? paint->getRunFlag()
: minikin::RunFlag::NONE;
@@ -105,7 +105,7 @@
const minikin::Range range(start, start + count);
const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit();
const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
- const minikin::RunFlag minikinRunFlag = text_feature::inter_character_justification()
+ const minikin::RunFlag minikinRunFlag = text_feature::letter_spacing_justification()
? paint->getRunFlag()
: minikin::RunFlag::NONE;
diff --git a/services/autofill/java/com/android/server/autofill/ui/BottomSheetButtonBarLayout.java b/services/autofill/java/com/android/server/autofill/ui/BottomSheetButtonBarLayout.java
new file mode 100644
index 0000000..69c7b29
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/BottomSheetButtonBarLayout.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+import com.android.internal.widget.ButtonBarLayout;
+
+/** An extension of {@link ButtonBarLayout} for use in Autofill bottom sheets. */
+public class BottomSheetButtonBarLayout extends ButtonBarLayout {
+
+ public BottomSheetButtonBarLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final View spacer = findViewById(R.id.autofill_button_bar_spacer);
+ if (spacer == null) {
+ return;
+ }
+ if (isStacked()) {
+ spacer.getLayoutParams().width = 0;
+ spacer.getLayoutParams().height =
+ getResources().getDimensionPixelSize(R.dimen.autofill_button_bar_spacer_height);
+ setGravity(Gravity.CENTER_VERTICAL | Gravity.END);
+ } else {
+ spacer.getLayoutParams().width =
+ getResources().getDimensionPixelSize(R.dimen.autofill_button_bar_spacer_width);
+ spacer.getLayoutParams().height = 0;
+ setGravity(Gravity.CENTER_VERTICAL);
+ }
+ }
+
+ private boolean isStacked() {
+ return getOrientation() == LinearLayout.VERTICAL;
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index c37e619..d1c8c30 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -152,6 +152,11 @@
private int mActiveIndex;
/**
+ * True if the broadcast actively being dispatched to this process was re-enqueued previously.
+ */
+ private boolean mActiveReEnqueued;
+
+ /**
* Count of {@link #mActive} broadcasts that have been dispatched since this
* queue was last idle.
*/
@@ -312,6 +317,7 @@
final SomeArgs broadcastArgs = SomeArgs.obtain();
broadcastArgs.arg1 = record;
broadcastArgs.argi1 = recordIndex;
+ broadcastArgs.argi2 = 1;
getQueueForBroadcast(record).addFirst(broadcastArgs);
onBroadcastEnqueued(record, recordIndex);
}
@@ -609,6 +615,7 @@
final SomeArgs next = removeNextBroadcast();
mActive = (BroadcastRecord) next.arg1;
mActiveIndex = next.argi1;
+ mActiveReEnqueued = (next.argi2 == 1);
mActiveCountSinceIdle++;
mActiveAssumedDeliveryCountSinceIdle +=
(mActive.isAssumedDelivered(mActiveIndex) ? 1 : 0);
@@ -624,12 +631,21 @@
public void makeActiveIdle() {
mActive = null;
mActiveIndex = 0;
+ mActiveReEnqueued = false;
mActiveCountSinceIdle = 0;
mActiveAssumedDeliveryCountSinceIdle = 0;
mActiveViaColdStart = false;
invalidateRunnableAt();
}
+ public boolean wasActiveBroadcastReEnqueued() {
+ // If the flag is not enabled, treat as if the broadcast was never re-enqueued.
+ if (!Flags.avoidRepeatedBcastReEnqueues()) {
+ return false;
+ }
+ return mActiveReEnqueued;
+ }
+
/**
* Update summary statistics when the given record has been enqueued.
*/
@@ -1476,6 +1492,9 @@
if (runningOomAdjusted) {
pw.print("runningOomAdjusted:"); pw.println(runningOomAdjusted);
}
+ if (mActiveReEnqueued) {
+ pw.print("activeReEnqueued:"); pw.println(mActiveReEnqueued);
+ }
}
@NeverCompile
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index db0f03f..98263df 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -542,8 +542,8 @@
updateOomAdj |= queue.runningOomAdjusted;
try {
completed = scheduleReceiverWarmLocked(queue);
- } catch (BroadcastDeliveryFailedException e) {
- reEnqueueActiveBroadcast(queue);
+ } catch (BroadcastRetryException e) {
+ finishOrReEnqueueActiveBroadcast(queue);
completed = true;
}
} else {
@@ -586,7 +586,12 @@
private void clearInvalidPendingColdStart() {
logw("Clearing invalid pending cold start: " + mRunningColdStart);
- mRunningColdStart.reEnqueueActiveBroadcast();
+ if (mRunningColdStart.wasActiveBroadcastReEnqueued()) {
+ finishReceiverActiveLocked(mRunningColdStart, BroadcastRecord.DELIVERY_FAILURE,
+ "invalid start with re-enqueued broadcast");
+ } else {
+ mRunningColdStart.reEnqueueActiveBroadcast();
+ }
demoteFromRunningLocked(mRunningColdStart);
clearRunningColdStart();
enqueueUpdateRunningList();
@@ -613,19 +618,26 @@
}
}
- private void reEnqueueActiveBroadcast(@NonNull BroadcastProcessQueue queue) {
+ private void finishOrReEnqueueActiveBroadcast(@NonNull BroadcastProcessQueue queue) {
checkState(queue.isActive(), "isActive");
- final BroadcastRecord record = queue.getActive();
- final int index = queue.getActiveIndex();
- setDeliveryState(queue, queue.app, record, index, record.receivers.get(index),
- BroadcastRecord.DELIVERY_PENDING, "reEnqueueActiveBroadcast");
- queue.reEnqueueActiveBroadcast();
+ if (queue.wasActiveBroadcastReEnqueued()) {
+ // If the broadcast was already re-enqueued previously, finish it to avoid repeated
+ // delivery attempts
+ finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
+ "re-enqueued broadcast delivery failed");
+ } else {
+ final BroadcastRecord record = queue.getActive();
+ final int index = queue.getActiveIndex();
+ setDeliveryState(queue, queue.app, record, index, record.receivers.get(index),
+ BroadcastRecord.DELIVERY_PENDING, "reEnqueueActiveBroadcast");
+ queue.reEnqueueActiveBroadcast();
+ }
}
@Override
public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)
- throws BroadcastDeliveryFailedException {
+ throws BroadcastRetryException {
if (DEBUG_BROADCAST) {
logv("Process " + app + " is attached");
}
@@ -653,8 +665,8 @@
if (scheduleReceiverWarmLocked(queue)) {
demoteFromRunningLocked(queue);
}
- } catch (BroadcastDeliveryFailedException e) {
- reEnqueueActiveBroadcast(queue);
+ } catch (BroadcastRetryException e) {
+ finishOrReEnqueueActiveBroadcast(queue);
demoteFromRunningLocked(queue);
throw e;
}
@@ -983,7 +995,7 @@
@CheckResult
@GuardedBy("mService")
private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue)
- throws BroadcastDeliveryFailedException {
+ throws BroadcastRetryException {
checkState(queue.isActive(), "isActive");
final int cookie = traceBegin("scheduleReceiverWarmLocked");
@@ -1065,7 +1077,7 @@
*/
@CheckResult
private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index) throws BroadcastDeliveryFailedException {
+ @NonNull BroadcastRecord r, int index) throws BroadcastRetryException {
final ProcessRecord app = queue.app;
final Object receiver = r.receivers.get(index);
@@ -1157,7 +1169,7 @@
// to try redelivering the broadcast to this receiver.
if (receiver instanceof ResolveInfo) {
cancelDeliveryTimeoutLocked(queue);
- throw new BroadcastDeliveryFailedException(e);
+ throw new BroadcastRetryException(e);
}
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"remote app");
@@ -1316,8 +1328,8 @@
demoteFromRunningLocked(queue);
return true;
}
- } catch (BroadcastDeliveryFailedException e) {
- reEnqueueActiveBroadcast(queue);
+ } catch (BroadcastRetryException e) {
+ finishOrReEnqueueActiveBroadcast(queue);
demoteFromRunningLocked(queue);
return true;
}
diff --git a/services/core/java/com/android/server/am/BroadcastRetryException.java b/services/core/java/com/android/server/am/BroadcastRetryException.java
new file mode 100644
index 0000000..8bd6664
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastRetryException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+/**
+ * Exception to represent that broadcast delivery failed and we should try redelivering it.
+ */
+public class BroadcastRetryException extends BroadcastDeliveryFailedException {
+ public BroadcastRetryException(String name) {
+ super(name);
+ }
+
+ public BroadcastRetryException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 862542e..7d82f0c 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1499,8 +1499,12 @@
&& !uidRec.isCurAllowListed()) {
// UID is now in the background (and not on the temp allowlist). Was it
// previously in the foreground (or on the temp allowlist)?
+ // Or, it wasn't in the foreground / allowlist, but its last background
+ // timestamp is also 0, this means it's never been in the
+ // foreground / allowlist since it's born at all.
if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
- || uidRec.isSetAllowListed()) {
+ || uidRec.isSetAllowListed()
+ || uidRec.getLastBackgroundTime() == 0) {
uidRec.setLastBackgroundTime(nowElapsed);
if (mService.mDeterministicUidIdle
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
@@ -1526,6 +1530,7 @@
uidRec.setIdle(false);
}
uidRec.setLastBackgroundTime(0);
+ uidRec.setLastIdleTime(0);
}
final boolean wasCached = uidRec.getSetProcState()
> ActivityManager.PROCESS_STATE_RECEIVER;
@@ -3700,12 +3705,14 @@
for (int i = N - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
final long bgTime = uidRec.getLastBackgroundTime();
- if (bgTime > 0 && !uidRec.isIdle()) {
+ final long idleTime = uidRec.getLastIdleTime();
+ if (bgTime > 0 && (!uidRec.isIdle() || idleTime == 0)) {
if (bgTime <= maxBgTime) {
EventLogTags.writeAmUidIdle(uidRec.getUid());
synchronized (mProcLock) {
uidRec.setIdle(true);
uidRec.setSetIdle(true);
+ uidRec.setLastIdleTime(nowElapsed);
}
mService.doStopUidLocked(uidRec.getUid(), uidRec);
} else {
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 4329afc..45fd470 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -66,6 +66,9 @@
private long mLastBackgroundTime;
@CompositeRWLock({"mService", "mProcLock"})
+ private long mLastIdleTime;
+
+ @CompositeRWLock({"mService", "mProcLock"})
private boolean mEphemeral;
@CompositeRWLock({"mService", "mProcLock"})
@@ -255,6 +258,16 @@
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getLastIdleTime() {
+ return mLastIdleTime;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setLastIdleTime(long lastActiveTime) {
+ mLastIdleTime = lastActiveTime;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
boolean isEphemeral() {
return mEphemeral;
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 31d9cc9..16dbe18 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -42,3 +42,14 @@
description: "Optimize the service bindings by different policies like skipping oom adjuster"
bug: "318717054"
}
+
+flag {
+ namespace: "backstage_power"
+ name: "avoid_repeated_bcast_re_enqueues"
+ description: "Avoid re-enqueueing a broadcast repeatedly"
+ bug: "319225224"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index d928306..9f97551 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -73,6 +73,7 @@
"testng",
"compatibility-device-util-axt",
"flag-junit",
+ "am_flags_lib",
],
libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index f875f65..fb47aa8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -38,6 +38,8 @@
import android.os.HandlerThread;
import android.os.TestLooperManager;
import android.os.UserHandle;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.util.SparseArray;
@@ -93,6 +95,9 @@
.spyStatic(ProcessList.class)
.build();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[1];
@Mock
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 75409d9..3f6117b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -75,6 +75,8 @@
import android.os.PowerExemptionManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.proto.ProtoOutputStream;
@@ -135,6 +137,17 @@
ProcessStartBehavior.SUCCESS);
/**
+ * Map of processes to behaviors indicating how the new processes should behave as needed
+ * by the tests.
+ */
+ private ArrayMap<String, ProcessBehavior> mNewProcessBehaviors = new ArrayMap<>();
+
+ /**
+ * Map of processes to behaviors indicating how the new process starts should result in.
+ */
+ private ArrayMap<String, ProcessStartBehavior> mNewProcessStartBehaviors = new ArrayMap<>();
+
+ /**
* Collection of all active processes during current test run.
*/
private List<ProcessRecord> mActiveProcesses = new ArrayList<>();
@@ -161,15 +174,17 @@
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
assertHealth();
- final ProcessStartBehavior behavior = mNextProcessStartBehavior
- .getAndSet(ProcessStartBehavior.SUCCESS);
+ final String processName = invocation.getArgument(0);
+ final ProcessStartBehavior behavior = mNewProcessStartBehaviors.getOrDefault(
+ processName, mNextProcessStartBehavior.getAndSet(ProcessStartBehavior.SUCCESS));
if (behavior == ProcessStartBehavior.FAIL_NULL) {
return null;
}
- final String processName = invocation.getArgument(0);
final ApplicationInfo ai = invocation.getArgument(1);
+ final ProcessBehavior processBehavior = mNewProcessBehaviors.getOrDefault(
+ processName, ProcessBehavior.NORMAL);
final ProcessRecord res = makeActiveProcessRecord(ai, processName,
- ProcessBehavior.NORMAL, UnaryOperator.identity());
+ processBehavior, UnaryOperator.identity());
final ProcessRecord deliverRes;
switch (behavior) {
case SUCCESS_PREDECESSOR:
@@ -274,6 +289,8 @@
assertEquals(app.toShortString(), ProcessList.SCHED_GROUP_UNDEFINED,
mQueue.getPreferredSchedulingGroupLocked(app));
}
+ mNewProcessBehaviors.clear();
+ mNewProcessStartBehaviors.clear();
}
@Override
@@ -955,6 +972,40 @@
}
/**
+ * Verify that we handle manifest receivers in a process that always
+ * responds with {@link DeadObjectException} even after restarting.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_AVOID_REPEATED_BCAST_RE_ENQUEUES)
+ public void testRepeatedDead_Manifest() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ mNewProcessBehaviors.put(PACKAGE_GREEN, ProcessBehavior.DEAD);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
+ withPriority(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0))));
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+ waitForIdle();
+
+ final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ // Modern queue always kills the target process when broadcast delivery fails, where as
+ // the legacy queue leaves the process killing task to AMS
+ if (mImpl == Impl.MODERN) {
+ assertNull(receiverGreenApp);
+ }
+ final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE));
+ verifyScheduleReceiver(receiverBlueApp, airplane);
+ final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW));
+ verifyScheduleReceiver(receiverYellowApp, timezone);
+ }
+
+ /**
* Verify that we handle the system failing to start a process.
*/
@Test
@@ -1142,6 +1193,49 @@
}
/**
+ * Verify that when BroadcastQueue doesn't get notified when a process gets killed repeatedly,
+ * it doesn't get stuck.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_AVOID_REPEATED_BCAST_RE_ENQUEUES)
+ public void testRepeatedKillWithoutNotify() throws Exception {
+ // Legacy queue does not handle repeated kills that don't get notified.
+ Assume.assumeTrue(mImpl == Impl.MODERN);
+
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+
+ mNewProcessStartBehaviors.put(PACKAGE_GREEN, ProcessStartBehavior.KILLED_WITHOUT_NOTIFY);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+ withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
+ withPriority(makeRegisteredReceiver(receiverBlueApp), 5),
+ withPriority(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), 0))));
+
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE))));
+
+ waitForIdle();
+ final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW));
+ final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
+ getUidForPackage(PACKAGE_ORANGE));
+
+ // Modern queue always kills the target process when broadcast delivery fails, where as
+ // the legacy queue leaves the process killing task to AMS
+ if (mImpl == Impl.MODERN) {
+ assertNull(receiverGreenApp);
+ }
+ verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
+ verifyScheduleReceiver(times(1), receiverYellowApp, airplane);
+ verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
+ }
+
+ /**
* Verify that a broadcast sent to a frozen app, which gets killed as part of unfreezing
* process due to pending sync binder transactions, is delivered as expected.
*/