summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/OWNERS1
-rw-r--r--core/java/android/app/slice/Slice.java11
-rw-r--r--core/java/android/os/BatteryStats.java102
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java574
-rw-r--r--core/java/android/os/Parcel.java19
-rw-r--r--core/java/android/permission/PermissionManager.java18
-rw-r--r--core/java/android/telephony/TelephonyCallback.java2
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig6
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java19
-rw-r--r--core/jni/android_view_MotionEvent.cpp3
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp5
-rw-r--r--core/tests/coretests/src/android/os/BinderProxyTest.java2
-rw-r--r--core/tests/coretests/src/android/os/MessageQueueTest.java32
-rw-r--r--core/tests/coretests/src/android/view/MotionEventTest.java25
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt138
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java66
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java324
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java188
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java8
-rw-r--r--libs/androidfw/Android.bp2
-rw-r--r--libs/androidfw/LocaleData.cpp58
-rw-r--r--libs/androidfw/LocaleDataLookup.cpp64
-rw-r--r--libs/androidfw/include/androidfw/LocaleDataLookup.h79
-rw-r--r--libs/androidfw/tests/LocaleDataLookup_test.cpp108
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java4
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java2
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java12
-rw-r--r--packages/SystemUI/res/drawable/volume_dialog_background.xml3
-rw-r--r--packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml22
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml25
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_drawer.xml10
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt289
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt5
-rw-r--r--services/accessibility/accessibility.aconfig7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java2
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java6
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java30
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java34
-rw-r--r--services/core/java/com/android/server/am/BroadcastHistory.java10
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java10
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java14
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.md4
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java (renamed from services/core/java/com/android/server/am/BroadcastQueueModernImpl.java)35
-rw-r--r--services/core/java/com/android/server/am/OWNERS1
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java7
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java26
-rw-r--r--services/core/java/com/android/server/power/stats/PowerStatsStore.java12
-rw-r--r--services/core/java/com/android/server/power/stats/flags.aconfig10
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java18
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java10
-rw-r--r--services/core/java/com/android/server/wm/Transition.java13
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp27
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt50
-rw-r--r--services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt11
-rw-r--r--services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt56
-rw-r--r--services/tests/mockingservicestests/Android.bp2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueImplTest.java (renamed from services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java)8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java8
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java73
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java4
85 files changed, 2131 insertions, 966 deletions
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 6e4c28f3eca6..7a811a1cdfb8 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -28,6 +28,7 @@ per-file ProfilerInfo* = file:/ACTIVITY_MANAGER_OWNERS
per-file Service* = file:/ACTIVITY_MANAGER_OWNERS
per-file SystemServiceRegistry.java = file:/ACTIVITY_MANAGER_OWNERS
per-file *UserSwitchObserver* = file:/ACTIVITY_MANAGER_OWNERS
+per-file UidObserver* = file:/ACTIVITY_MANAGER_OWNERS
# UI Automation
per-file *UiAutomation* = file:/services/accessibility/OWNERS
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 5514868a32f5..382f9bee5eeb 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -273,11 +273,7 @@ public final class Slice implements Parcelable {
protected Slice(Parcel in) {
mHints = in.readStringArray();
- int n = in.readInt();
- mItems = new SliceItem[n];
- for (int i = 0; i < n; i++) {
- mItems[i] = SliceItem.CREATOR.createFromParcel(in);
- }
+ mItems = in.createTypedArray(SliceItem.CREATOR);
mUri = Uri.CREATOR.createFromParcel(in);
mSpec = in.readTypedObject(SliceSpec.CREATOR);
}
@@ -313,10 +309,7 @@ public final class Slice implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(mHints);
- dest.writeInt(mItems.length);
- for (int i = 0; i < mItems.length; i++) {
- mItems[i].writeToParcel(dest, flags);
- }
+ dest.writeTypedArray(mItems, flags);
mUri.writeToParcel(dest, 0);
dest.writeTypedObject(mSpec, flags);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c41e626444c9..298cec1674b2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -69,16 +69,19 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
/**
* A class providing access to battery usage statistics, including information on
@@ -1868,6 +1871,11 @@ public abstract class BatteryStats {
@UnsupportedAppUsage
public long time;
+ // Wall clock time of the event, GMT. Unlike `time`, this timestamp is affected
+ // by changes in the clock setting. When the wall clock is adjusted, BatteryHistory
+ // records an event of type `CMD_CURRENT_TIME` or `CMD_RESET`.
+ public long currentTime;
+
@UnsupportedAppUsage
public static final byte CMD_UPDATE = 0; // These can be written as deltas
public static final byte CMD_NULL = -1;
@@ -2108,9 +2116,6 @@ public abstract class BatteryStats {
public int eventCode;
public HistoryTag eventTag;
- // Only set for CMD_CURRENT_TIME or CMD_RESET, as per System.currentTimeMillis().
- public long currentTime;
-
// Meta-data when reading.
public int numReadInts;
@@ -6926,6 +6931,23 @@ public abstract class BatteryStats {
}
public static class HistoryPrinter {
+ private static final int FORMAT_LEGACY = 1;
+
+ // This constant MUST be incremented whenever the history dump format changes.
+ private static final int FORMAT_VERSION = 2;
+
+ private final SimpleDateFormat mHistoryItemTimestampFormat =
+ new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
+ private final SimpleDateFormat mCurrentTimeEventTimeFormat =
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
+
+ // This API is error prone, but we are making an exception here to avoid excessive
+ // object allocations.
+ @SuppressWarnings("JavaUtilDate")
+ private final Date mDate = new Date();
+
+ private final int mFormatVersion;
+
int oldState = 0;
int oldState2 = 0;
int oldLevel = -1;
@@ -6939,6 +6961,22 @@ public abstract class BatteryStats {
double oldWifiRailChargeMah = -1;
long lastTime = -1;
+ public HistoryPrinter() {
+ this(TimeZone.getDefault());
+ }
+
+ public HistoryPrinter(TimeZone timeZone) {
+ this(com.android.server.power.optimization.Flags
+ .extendedBatteryHistoryContinuousCollectionEnabled()
+ ? FORMAT_VERSION : FORMAT_LEGACY, timeZone);
+ }
+
+ private HistoryPrinter(int formatVersion, TimeZone timeZone) {
+ mFormatVersion = formatVersion;
+ mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone);
+ mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone);
+ }
+
void reset() {
oldState = oldState2 = 0;
oldLevel = -1;
@@ -6966,16 +7004,22 @@ public abstract class BatteryStats {
}
}
+ @SuppressWarnings("JavaUtilDate")
private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
StringBuilder item = new StringBuilder();
if (!checkin) {
item.append(" ");
- TimeUtils.formatDuration(
- rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- item.append(" (");
- item.append(rec.numReadInts);
- item.append(") ");
+ if (mFormatVersion == FORMAT_LEGACY) {
+ TimeUtils.formatDuration(
+ rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+ item.append(" (");
+ item.append(rec.numReadInts);
+ item.append(") ");
+ } else {
+ mDate.setTime(rec.currentTime);
+ item.append(mHistoryItemTimestampFormat.format(mDate)).append(' ');
+ }
} else {
item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
item.append(HISTORY_DATA); item.append(',');
@@ -7007,8 +7051,8 @@ public abstract class BatteryStats {
item.append("\n");
} else {
item.append(" ");
- item.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
- rec.currentTime).toString());
+ mDate.setTime(rec.currentTime);
+ item.append(mCurrentTimeEventTimeFormat.format(mDate));
item.append("\n");
}
} else if (rec.cmd == HistoryItem.CMD_SHUTDOWN) {
@@ -7529,11 +7573,31 @@ public abstract class BatteryStats {
public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6;
private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) {
+ final HistoryPrinter hprinter = new HistoryPrinter();
synchronized (this) {
- dumpHistoryTagPoolLocked(pw, checkin);
+ if (!checkin) {
+ final long historyTotalSize = getHistoryTotalSize();
+ final long historyUsedSize = getHistoryUsedSize();
+ pw.print("Battery History");
+ if (hprinter.mFormatVersion != HistoryPrinter.FORMAT_LEGACY) {
+ pw.print(" [Format: " + hprinter.mFormatVersion + "]");
+ }
+ pw.print(" (");
+ pw.print((100 * historyUsedSize) / historyTotalSize);
+ pw.print("% used, ");
+ printSizeValue(pw, historyUsedSize);
+ pw.print(" used of ");
+ printSizeValue(pw, historyTotalSize);
+ pw.print(", ");
+ pw.print(getHistoryStringPoolSize());
+ pw.print(" strings using ");
+ printSizeValue(pw, getHistoryStringPoolBytes());
+ pw.println("):");
+ } else {
+ dumpHistoryTagPoolLocked(pw, checkin);
+ }
}
- final HistoryPrinter hprinter = new HistoryPrinter();
long lastTime = -1;
long baseTime = -1;
boolean printed = false;
@@ -7645,20 +7709,6 @@ public abstract class BatteryStats {
pw.print("\"");
pw.println();
}
- } else {
- final long historyTotalSize = getHistoryTotalSize();
- final long historyUsedSize = getHistoryUsedSize();
- pw.print("Battery History (");
- pw.print((100 * historyUsedSize) / historyTotalSize);
- pw.print("% used, ");
- printSizeValue(pw, historyUsedSize);
- pw.print(" used of ");
- printSizeValue(pw, historyTotalSize);
- pw.print(", ");
- pw.print(getHistoryStringPoolSize());
- pw.print(" strings using ");
- printSizeValue(pw, getHistoryStringPoolBytes());
- pw.println("):");
}
}
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 230fa3fec930..d9969d8b9596 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -40,6 +40,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.ravenwood.RavenwoodEnvironment;
import dalvik.annotation.optimization.NeverCompile;
+import dalvik.annotation.optimization.NeverInline;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -237,6 +238,46 @@ public final class MessageQueue {
}
private final MatchDeliverableMessages mMatchDeliverableMessages =
new MatchDeliverableMessages();
+
+ @NeverInline
+ private boolean isIdleConcurrent() {
+ final long now = SystemClock.uptimeMillis();
+
+ if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
+ return false;
+ }
+
+ MessageNode msgNode = null;
+ MessageNode asyncMsgNode = null;
+
+ if (!mPriorityQueue.isEmpty()) {
+ try {
+ msgNode = mPriorityQueue.first();
+ } catch (NoSuchElementException e) { }
+ }
+
+ if (!mAsyncPriorityQueue.isEmpty()) {
+ try {
+ asyncMsgNode = mAsyncPriorityQueue.first();
+ } catch (NoSuchElementException e) { }
+ }
+
+ if ((msgNode != null && msgNode.getWhen() <= now)
+ || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @NeverInline
+ private boolean isIdleLegacy() {
+ synchronized (this) {
+ final long now = SystemClock.uptimeMillis();
+ return mMessages == null || now < mMessages.when;
+ }
+ }
+
/**
* Returns true if the looper has no pending messages which are due to be processed.
*
@@ -246,38 +287,23 @@ public final class MessageQueue {
*/
public boolean isIdle() {
if (mUseConcurrent) {
- final long now = SystemClock.uptimeMillis();
-
- if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
- return false;
- }
-
- MessageNode msgNode = null;
- MessageNode asyncMsgNode = null;
-
- if (!mPriorityQueue.isEmpty()) {
- try {
- msgNode = mPriorityQueue.first();
- } catch (NoSuchElementException e) { }
- }
-
- if (!mAsyncPriorityQueue.isEmpty()) {
- try {
- asyncMsgNode = mAsyncPriorityQueue.first();
- } catch (NoSuchElementException e) { }
- }
+ return isIdleConcurrent();
+ } else {
+ return isIdleLegacy();
+ }
+ }
- if ((msgNode != null && msgNode.getWhen() <= now)
- || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) {
- return false;
- }
+ @NeverInline
+ private void addIdleHandlerConcurrent(@NonNull IdleHandler handler) {
+ synchronized (mIdleHandlersLock) {
+ mIdleHandlers.add(handler);
+ }
+ }
- return true;
- } else {
- synchronized (this) {
- final long now = SystemClock.uptimeMillis();
- return mMessages == null || now < mMessages.when;
- }
+ @NeverInline
+ private void addIdleHandlerLegacy(@NonNull IdleHandler handler) {
+ synchronized (this) {
+ mIdleHandlers.add(handler);
}
}
@@ -296,13 +322,23 @@ public final class MessageQueue {
throw new NullPointerException("Can't add a null IdleHandler");
}
if (mUseConcurrent) {
- synchronized (mIdleHandlersLock) {
- mIdleHandlers.add(handler);
- }
+ addIdleHandlerConcurrent(handler);
} else {
- synchronized (this) {
- mIdleHandlers.add(handler);
- }
+ addIdleHandlerLegacy(handler);
+ }
+ }
+
+ @NeverInline
+ private void removeIdleHandlerConcurrent(@NonNull IdleHandler handler) {
+ synchronized (mIdleHandlersLock) {
+ mIdleHandlers.remove(handler);
+ }
+ }
+
+ @NeverInline
+ private void removeIdleHandlerLegacy(@NonNull IdleHandler handler) {
+ synchronized (this) {
+ mIdleHandlers.remove(handler);
}
}
@@ -317,13 +353,23 @@ public final class MessageQueue {
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
if (mUseConcurrent) {
- synchronized (mIdleHandlersLock) {
- mIdleHandlers.remove(handler);
- }
+ removeIdleHandlerConcurrent(handler);
} else {
- synchronized (this) {
- mIdleHandlers.remove(handler);
- }
+ removeIdleHandlerLegacy(handler);
+ }
+ }
+
+ @NeverInline
+ private boolean isPollingConcurrent() {
+ // If the loop is quitting then it must not be idling.
+ // We can assume mPtr != 0 when sQuitting is false.
+ return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
+ }
+
+ @NeverInline
+ private boolean isPollingLegacy() {
+ synchronized (this) {
+ return isPollingLocked();
}
}
@@ -340,13 +386,9 @@ public final class MessageQueue {
*/
public boolean isPolling() {
if (mUseConcurrent) {
- // If the loop is quitting then it must not be idling.
- // We can assume mPtr != 0 when sQuitting is false.
- return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
+ return isPollingConcurrent();
} else {
- synchronized (this) {
- return isPollingLocked();
- }
+ return isPollingLegacy();
}
}
@@ -355,6 +397,23 @@ public final class MessageQueue {
// We can assume mPtr != 0 when mQuitting is false.
return !mQuitting && nativeIsPolling(mPtr);
}
+ @NeverInline
+ private void addOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd,
+ @OnFileDescriptorEventListener.Events int events,
+ @NonNull OnFileDescriptorEventListener listener) {
+ synchronized (mFileDescriptorRecordsLock) {
+ updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+ }
+ }
+
+ @NeverInline
+ private void addOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd,
+ @OnFileDescriptorEventListener.Events int events,
+ @NonNull OnFileDescriptorEventListener listener) {
+ synchronized (this) {
+ updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+ }
+ }
/**
* Adds a file descriptor listener to receive notification when file descriptor
@@ -391,13 +450,23 @@ public final class MessageQueue {
}
if (mUseConcurrent) {
- synchronized (mFileDescriptorRecordsLock) {
- updateOnFileDescriptorEventListenerLocked(fd, events, listener);
- }
+ addOnFileDescriptorEventListenerConcurrent(fd, events, listener);
} else {
- synchronized (this) {
- updateOnFileDescriptorEventListenerLocked(fd, events, listener);
- }
+ addOnFileDescriptorEventListenerLegacy(fd, events, listener);
+ }
+ }
+
+ @NeverInline
+ private void removeOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd) {
+ synchronized (mFileDescriptorRecordsLock) {
+ updateOnFileDescriptorEventListenerLocked(fd, 0, null);
+ }
+ }
+
+ @NeverInline
+ private void removeOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd) {
+ synchronized (this) {
+ updateOnFileDescriptorEventListenerLocked(fd, 0, null);
}
}
@@ -419,13 +488,9 @@ public final class MessageQueue {
throw new IllegalArgumentException("fd must not be null");
}
if (mUseConcurrent) {
- synchronized (mFileDescriptorRecordsLock) {
- updateOnFileDescriptorEventListenerLocked(fd, 0, null);
- }
+ removeOnFileDescriptorEventListenerConcurrent(fd);
} else {
- synchronized (this) {
- updateOnFileDescriptorEventListenerLocked(fd, 0, null);
- }
+ removeOnFileDescriptorEventListenerLegacy(fd);
}
}
@@ -732,6 +797,7 @@ public final class MessageQueue {
}
}
+ @NeverInline
private Message nextConcurrent() {
final long ptr = mPtr;
if (ptr == 0) {
@@ -806,12 +872,8 @@ public final class MessageQueue {
}
}
- @UnsupportedAppUsage
- Message next() {
- if (mUseConcurrent) {
- return nextConcurrent();
- }
-
+ @NeverInline
+ private Message nextLegacy() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
@@ -929,6 +991,15 @@ public final class MessageQueue {
}
}
+ @UnsupportedAppUsage
+ Message next() {
+ if (mUseConcurrent) {
+ return nextConcurrent();
+ } else {
+ return nextLegacy();
+ }
+ }
+
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
@@ -966,6 +1037,17 @@ public final class MessageQueue {
}
}
+ @NeverInline
+ private int postSyncBarrierConcurrent() {
+ return postSyncBarrier(SystemClock.uptimeMillis());
+
+ }
+
+ @NeverInline
+ private int postSyncBarrierLegacy() {
+ return postSyncBarrier(SystemClock.uptimeMillis());
+ }
+
/**
* Posts a synchronization barrier to the Looper's message queue.
*
@@ -992,7 +1074,11 @@ public final class MessageQueue {
@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
- return postSyncBarrier(SystemClock.uptimeMillis());
+ if (mUseConcurrent) {
+ return postSyncBarrierConcurrent();
+ } else {
+ return postSyncBarrierLegacy();
+ }
}
private int postSyncBarrier(long when) {
@@ -1077,48 +1163,35 @@ public final class MessageQueue {
}
}
- /**
- * Removes a synchronization barrier.
- *
- * @param token The synchronization barrier token that was returned by
- * {@link #postSyncBarrier}.
- *
- * @throws IllegalStateException if the barrier was not found.
- *
- * @hide
- */
- @UnsupportedAppUsage
- @TestApi
- public void removeSyncBarrier(int token) {
- // Remove a sync barrier token from the queue.
- // If the queue is no longer stalled by a barrier then wake it.
- if (mUseConcurrent) {
- boolean removed;
- MessageNode first;
- final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
-
- try {
- /* Retain the first element to see if we are currently stuck on a barrier. */
- first = mPriorityQueue.first();
- } catch (NoSuchElementException e) {
- /* The queue is empty */
- first = null;
- }
+ @NeverInline
+ private void removeSyncBarrierConcurrent(int token) {
+ boolean removed;
+ MessageNode first;
+ final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
- removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true);
- if (removed && first != null) {
- Message m = first.mMessage;
- if (m.target == null && m.arg1 == token) {
- /* Wake up next() in case it was sleeping on this barrier. */
- nativeWake(mPtr);
- }
- } else if (!removed) {
- throw new IllegalStateException("The specified message queue synchronization "
- + " barrier token has not been posted or has already been removed.");
+ try {
+ /* Retain the first element to see if we are currently stuck on a barrier. */
+ first = mPriorityQueue.first();
+ } catch (NoSuchElementException e) {
+ /* The queue is empty */
+ first = null;
+ }
+
+ removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true);
+ if (removed && first != null) {
+ Message m = first.mMessage;
+ if (m.target == null && m.arg1 == token) {
+ /* Wake up next() in case it was sleeping on this barrier. */
+ nativeWake(mPtr);
}
- return;
+ } else if (!removed) {
+ throw new IllegalStateException("The specified message queue synchronization "
+ + " barrier token has not been posted or has already been removed.");
}
+ }
+ @NeverInline
+ private void removeSyncBarrierLegacy(int token) {
synchronized (this) {
Message prev = null;
Message p = mMessages;
@@ -1154,19 +1227,40 @@ public final class MessageQueue {
}
}
- boolean enqueueMessage(Message msg, long when) {
- if (msg.target == null) {
- throw new IllegalArgumentException("Message must have a target.");
+ /**
+ * Removes a synchronization barrier.
+ *
+ * @param token The synchronization barrier token that was returned by
+ * {@link #postSyncBarrier}.
+ *
+ * @throws IllegalStateException if the barrier was not found.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ @TestApi
+ public void removeSyncBarrier(int token) {
+ // Remove a sync barrier token from the queue.
+ // If the queue is no longer stalled by a barrier then wake it.
+ if (mUseConcurrent) {
+ removeSyncBarrierConcurrent(token);
+ } else {
+ removeSyncBarrierLegacy(token);
}
- if (mUseConcurrent) {
- if (msg.isInUse()) {
- throw new IllegalStateException(msg + " This message is already in use.");
- }
+ }
- return enqueueMessageUnchecked(msg, when);
+ @NeverInline
+ private boolean enqueueMessageConcurrent(Message msg, long when) {
+ if (msg.isInUse()) {
+ throw new IllegalStateException(msg + " This message is already in use.");
}
+ return enqueueMessageUnchecked(msg, when);
+ }
+
+ @NeverInline
+ private boolean enqueueMessageLegacy(Message msg, long when) {
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
@@ -1272,6 +1366,18 @@ public final class MessageQueue {
return true;
}
+ boolean enqueueMessage(Message msg, long when) {
+ if (msg.target == null) {
+ throw new IllegalArgumentException("Message must have a target.");
+ }
+
+ if (mUseConcurrent) {
+ return enqueueMessageConcurrent(msg, when);
+ } else {
+ return enqueueMessageLegacy(msg, when);
+ }
+ }
+
private Message legacyPeekOrPoll(boolean peek) {
synchronized (this) {
// Try to retrieve the next message. Return if found.
@@ -1432,14 +1538,15 @@ public final class MessageQueue {
}
private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject =
new MatchHandlerWhatAndObject();
- boolean hasMessages(Handler h, int what, Object object) {
- if (h == null) {
- return false;
- }
- if (mUseConcurrent) {
- return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
- false);
- }
+
+ @NeverInline
+ private boolean hasMessagesConcurrent(Handler h, int what, Object object) {
+ return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
+ false);
+ }
+
+ @NeverInline
+ private boolean hasMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
while (p != null) {
@@ -1452,6 +1559,17 @@ public final class MessageQueue {
}
}
+ boolean hasMessages(Handler h, int what, Object object) {
+ if (h == null) {
+ return false;
+ }
+ if (mUseConcurrent) {
+ return hasMessagesConcurrent(h, what, object);
+ } else {
+ return hasMessagesLegacy(h, what, object);
+ }
+ }
+
private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1465,15 +1583,15 @@ public final class MessageQueue {
}
private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals =
new MatchHandlerWhatAndObjectEquals();
- boolean hasEqualMessages(Handler h, int what, Object object) {
- if (h == null) {
- return false;
- }
- if (mUseConcurrent) {
- return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
- false);
- }
+ @NeverInline
+ private boolean hasEqualMessagesConcurrent(Handler h, int what, Object object) {
+ return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
+ false);
+ }
+
+ @NeverInline
+ private boolean hasEqualMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
while (p != null) {
@@ -1486,6 +1604,17 @@ public final class MessageQueue {
}
}
+ boolean hasEqualMessages(Handler h, int what, Object object) {
+ if (h == null) {
+ return false;
+ }
+ if (mUseConcurrent) {
+ return hasEqualMessagesConcurrent(h, what, object);
+ } else {
+ return hasEqualMessagesLegacy(h, what, object);
+ }
+ }
+
private static final class MatchHandlerRunnableAndObject extends MessageCompare {
@Override
public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1499,16 +1628,15 @@ public final class MessageQueue {
}
private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject =
new MatchHandlerRunnableAndObject();
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- boolean hasMessages(Handler h, Runnable r, Object object) {
- if (h == null) {
- return false;
- }
- if (mUseConcurrent) {
- return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
- false);
- }
+ @NeverInline
+ private boolean hasMessagesConcurrent(Handler h, Runnable r, Object object) {
+ return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
+ false);
+ }
+
+ @NeverInline
+ private boolean hasMessagesLegacy(Handler h, Runnable r, Object object) {
synchronized (this) {
Message p = mMessages;
while (p != null) {
@@ -1521,6 +1649,18 @@ public final class MessageQueue {
}
}
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ boolean hasMessages(Handler h, Runnable r, Object object) {
+ if (h == null) {
+ return false;
+ }
+ if (mUseConcurrent) {
+ return hasMessagesConcurrent(h, r, object);
+ } else {
+ return hasMessagesLegacy(h, r, object);
+ }
+ }
+
private static final class MatchHandler extends MessageCompare {
@Override
public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1529,13 +1669,14 @@ public final class MessageQueue {
}
}
private final MatchHandler mMatchHandler = new MatchHandler();
- boolean hasMessages(Handler h) {
- if (h == null) {
- return false;
- }
- if (mUseConcurrent) {
- return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
- }
+
+ @NeverInline
+ private boolean hasMessagesConcurrent(Handler h) {
+ return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
+ }
+
+ @NeverInline
+ private boolean hasMessagesLegacy(Handler h) {
synchronized (this) {
Message p = mMessages;
while (p != null) {
@@ -1548,14 +1689,24 @@ public final class MessageQueue {
}
}
- void removeMessages(Handler h, int what, Object object) {
+ boolean hasMessages(Handler h) {
if (h == null) {
- return;
+ return false;
}
if (mUseConcurrent) {
- findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
- return;
+ return hasMessagesConcurrent(h);
+ } else {
+ return hasMessagesLegacy(h);
}
+ }
+
+ @NeverInline
+ private void removeMessagesConcurrent(Handler h, int what, Object object) {
+ findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
+ }
+
+ @NeverInline
+ private void removeMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1598,16 +1749,24 @@ public final class MessageQueue {
}
}
- void removeEqualMessages(Handler h, int what, Object object) {
+ void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
-
if (mUseConcurrent) {
- findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
- return;
+ removeMessagesConcurrent(h, what, object);
+ } else {
+ removeMessagesLegacy(h, what, object);
}
+ }
+ @NeverInline
+ private void removeEqualMessagesConcurrent(Handler h, int what, Object object) {
+ findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
+ }
+
+ @NeverInline
+ private void removeEqualMessagesLegacy(Handler h, int what, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1650,15 +1809,25 @@ public final class MessageQueue {
}
}
- void removeMessages(Handler h, Runnable r, Object object) {
- if (h == null || r == null) {
+ void removeEqualMessages(Handler h, int what, Object object) {
+ if (h == null) {
return;
}
if (mUseConcurrent) {
- findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
- return;
+ removeEqualMessagesConcurrent(h, what, object);
+ } else {
+ removeEqualMessagesLegacy(h, what, object);
}
+ }
+
+ @NeverInline
+ private void removeMessagesConcurrent(Handler h, Runnable r, Object object) {
+ findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
+ }
+
+ @NeverInline
+ private void removeMessagesLegacy(Handler h, Runnable r, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1701,6 +1870,18 @@ public final class MessageQueue {
}
}
+ void removeMessages(Handler h, Runnable r, Object object) {
+ if (h == null || r == null) {
+ return;
+ }
+
+ if (mUseConcurrent) {
+ removeMessagesConcurrent(h, r, object);
+ } else {
+ removeMessagesLegacy(h, r, object);
+ }
+ }
+
private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1714,15 +1895,14 @@ public final class MessageQueue {
}
private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals =
new MatchHandlerRunnableAndObjectEquals();
- void removeEqualMessages(Handler h, Runnable r, Object object) {
- if (h == null || r == null) {
- return;
- }
- if (mUseConcurrent) {
- findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
- return;
- }
+ @NeverInline
+ private void removeEqualMessagesConcurrent(Handler h, Runnable r, Object object) {
+ findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
+ }
+
+ @NeverInline
+ private void removeEqualMessagesLegacy(Handler h, Runnable r, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1765,6 +1945,18 @@ public final class MessageQueue {
}
}
+ void removeEqualMessages(Handler h, Runnable r, Object object) {
+ if (h == null || r == null) {
+ return;
+ }
+
+ if (mUseConcurrent) {
+ removeEqualMessagesConcurrent(h, r, object);
+ } else {
+ removeEqualMessagesLegacy(h, r, object);
+ }
+ }
+
private static final class MatchHandlerAndObject extends MessageCompare {
@Override
public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1777,15 +1969,14 @@ public final class MessageQueue {
}
}
private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject();
- void removeCallbacksAndMessages(Handler h, Object object) {
- if (h == null) {
- return;
- }
- if (mUseConcurrent) {
+ @NeverInline
+ private void removeCallbacksAndMessagesConcurrent(Handler h, Object object) {
findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
- return;
- }
+ }
+
+ @NeverInline
+ private void removeCallbacksAndMessagesLegacy(Handler h, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1827,6 +2018,18 @@ public final class MessageQueue {
}
}
+ void removeCallbacksAndMessages(Handler h, Object object) {
+ if (h == null) {
+ return;
+ }
+
+ if (mUseConcurrent) {
+ removeCallbacksAndMessagesConcurrent(h, object);
+ } else {
+ removeCallbacksAndMessagesLegacy(h, object);
+ }
+ }
+
private static final class MatchHandlerAndObjectEquals extends MessageCompare {
@Override
public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1840,15 +2043,14 @@ public final class MessageQueue {
}
private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals =
new MatchHandlerAndObjectEquals();
- void removeCallbacksAndEqualMessages(Handler h, Object object) {
- if (h == null) {
- return;
- }
- if (mUseConcurrent) {
- findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
- return;
- }
+ @NeverInline
+ void removeCallbacksAndEqualMessagesConcurrent(Handler h, Object object) {
+ findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
+ }
+
+ @NeverInline
+ void removeCallbacksAndEqualMessagesLegacy(Handler h, Object object) {
synchronized (this) {
Message p = mMessages;
@@ -1890,6 +2092,18 @@ public final class MessageQueue {
}
}
+ void removeCallbacksAndEqualMessages(Handler h, Object object) {
+ if (h == null) {
+ return;
+ }
+
+ if (mUseConcurrent) {
+ removeCallbacksAndEqualMessagesConcurrent(h, object);
+ } else {
+ removeCallbacksAndEqualMessagesLegacy(h, object);
+ }
+ }
+
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 5ba6553a58c9..0879118ff856 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3668,6 +3668,7 @@ public final class Parcel {
int length = readInt();
if (length >= 0)
{
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, length);
array = new CharSequence[length];
for (int i = 0 ; i < length ; i++)
@@ -3689,6 +3690,7 @@ public final class Parcel {
int length = readInt();
if (length >= 0) {
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, length);
array = new ArrayList<CharSequence>(length);
for (int i = 0 ; i < length ; i++) {
@@ -3831,6 +3833,7 @@ public final class Parcel {
if (N < 0) {
return null;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
SparseBooleanArray sa = new SparseBooleanArray(N);
readSparseBooleanArrayInternal(sa, N);
return sa;
@@ -3847,6 +3850,7 @@ public final class Parcel {
if (N < 0) {
return null;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
SparseIntArray sa = new SparseIntArray(N);
readSparseIntArrayInternal(sa, N);
return sa;
@@ -3892,6 +3896,7 @@ public final class Parcel {
public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
int M = list.size();
int N = readInt();
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
int i = 0;
for (; i < M && i < N; i++) {
list.set(i, readTypedObject(c));
@@ -4050,6 +4055,7 @@ public final class Parcel {
public final void readStringList(@NonNull List<String> list) {
int M = list.size();
int N = readInt();
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
int i = 0;
for (; i < M && i < N; i++) {
list.set(i, readString());
@@ -4071,6 +4077,7 @@ public final class Parcel {
public final void readBinderList(@NonNull List<IBinder> list) {
int M = list.size();
int N = readInt();
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
int i = 0;
for (; i < M && i < N; i++) {
list.set(i, readStrongBinder());
@@ -4093,6 +4100,7 @@ public final class Parcel {
@NonNull Function<IBinder, T> asInterface) {
int M = list.size();
int N = readInt();
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
int i = 0;
for (; i < M && i < N; i++) {
list.set(i, asInterface.apply(readStrongBinder()));
@@ -4159,6 +4167,7 @@ public final class Parcel {
list.clear();
return list;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
final int m = list.size();
int i = 0;
@@ -5540,6 +5549,7 @@ public final class Parcel {
if (n < 0) {
return null;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
HashMap<K, V> map = new HashMap<>(n);
readMapInternal(map, n, loader, clazzKey, clazzValue);
return map;
@@ -5555,6 +5565,8 @@ public final class Parcel {
private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
@Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
@Nullable Class<V> clazzValue) {
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
+ // TODO: move all reservation of map size here, not all reserves?
while (n > 0) {
K key = readValue(loader, clazzKey);
V value = readValue(loader, clazzValue);
@@ -5580,6 +5592,7 @@ public final class Parcel {
*/
void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
boolean lazy, @Nullable ClassLoaderProvider loaderProvider, int[] lazyValueCount) {
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, size);
while (size > 0) {
String key = readString();
Object value = (lazy) ? readLazyValue(loaderProvider) : readValue(
@@ -5625,6 +5638,7 @@ public final class Parcel {
if (size < 0) {
return null;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, size);
ArraySet<Object> result = new ArraySet<>(size);
for (int i = 0; i < size; i++) {
Object value = readValue(loader);
@@ -5646,6 +5660,8 @@ public final class Parcel {
*/
private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
+ // TODO: move all size reservations here, instead of code that calls this. Not all reserves.
while (n > 0) {
T value = readValue(loader, clazz);
//Log.d(TAG, "Unmarshalling value=" + value);
@@ -5665,6 +5681,7 @@ public final class Parcel {
if (n < 0) {
return null;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
ArrayList<T> l = new ArrayList<>(n);
readListInternal(l, n, loader, clazz);
return l;
@@ -5707,6 +5724,7 @@ public final class Parcel {
*/
private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
@Nullable ClassLoader loader) {
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
while (N > 0) {
int key = readInt();
Object value = readValue(loader);
@@ -5725,6 +5743,7 @@ public final class Parcel {
if (n < 0) {
return null;
}
+ ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
SparseArray<T> outVal = new SparseArray<>(n);
while (n > 0) {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index bdf8d23438df..343d7527ea98 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1716,20 +1716,14 @@ public final class PermissionManager {
private static int checkPermissionUncached(@Nullable String permission, int pid, int uid,
int deviceId) {
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
final IActivityManager am = ActivityManager.getService();
if (am == null) {
- // Well this is super awkward; we somehow don't have an active ActivityManager
- // instance. If we're testing a root or system UID, then they totally have whatever
- // permission this is.
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
- if (sShouldWarnMissingActivityManager) {
- Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " holds "
- + permission);
- sShouldWarnMissingActivityManager = false;
- }
- return PackageManager.PERMISSION_GRANTED;
- }
+ // We don't have an active ActivityManager instance and the calling UID is not root or
+ // system, so we don't grant this permission.
Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+ permission);
return PackageManager.PERMISSION_DENIED;
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index e8b32ce5e314..cc19e7cab537 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -1009,7 +1009,7 @@ public class TelephonyCallback {
*/
public interface CellInfoListener {
/**
- * Callback invoked when a observed cell info has changed or new cells have been added
+ * Callback invoked when an observed cell info has changed or new cells have been added
* or removed on the registered subscription.
* Note, the registration subscription ID s from {@link TelephonyManager} object
* which registers TelephonyCallback by
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index abd93cfaf179..b38feeef290b 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -1,8 +1,10 @@
+# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+# proto-message: flag_declarations
+# Project link: http://gantry/projects/android_platform_windowing_sdk
+
package: "com.android.window.flags"
container: "system"
-# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
-
flag {
namespace: "windowing_sdk"
name: "activity_embedding_overlay_presentation_flag"
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index e2005d7cdedf..ee897cd37cd3 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -45,6 +45,8 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
private boolean mNextItemReady;
private boolean mTimeInitialized;
private boolean mClosed;
+ private long mBaseMonotonicTime;
+ private long mBaseTimeUtc;
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
long endTimeMs) {
@@ -84,24 +86,25 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
}
if (!mTimeInitialized) {
- mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+ mBaseMonotonicTime = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+ mHistoryItem.time = mBaseMonotonicTime;
mTimeInitialized = true;
}
- final long lastMonotonicTimeMs = mHistoryItem.time;
- final long lastWalltimeMs = mHistoryItem.currentTime;
try {
readHistoryDelta(p, mHistoryItem);
} catch (Throwable t) {
Slog.wtf(TAG, "Corrupted battery history", t);
break;
}
- if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
- && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET
- && lastWalltimeMs != 0) {
- mHistoryItem.currentTime =
- lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs);
+
+ if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
+ || mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_RESET) {
+ mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime);
}
+
+ mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime);
+
if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) {
break;
}
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 240be3fe5534..c105a6098870 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -704,7 +704,8 @@ static void android_view_MotionEvent_nativeSetFlags(CRITICAL_JNI_PARAMS_COMMA jl
jint flags) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
// Prevent private flags from being used from Java.
- event->setFlags(flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK);
+ const int32_t privateFlags = event->getFlags() & AMOTION_EVENT_PRIVATE_FLAG_MASK;
+ event->setFlags((flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK) | privateFlags);
}
static jint android_view_MotionEvent_nativeGetEdgeFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index f40cfd9f8e51..3108f1f2c7c5 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -314,8 +314,9 @@ static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zip
when, uncompLen, crc);
}
- ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
- "from apk.\n",
+ ALOGE("extractNativeLibs=false library '%s' is not PAGE(%zu)-"
+ "aligned within apk (APK alignment, not ELF alignment) -"
+ "will not be able to open it directly from apk.\n",
fileName, kPageSize);
return INSTALL_FAILED_INVALID_APK;
}
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index 335791c031b4..5fff0b8d0849 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -138,7 +138,7 @@ public class BinderProxyTest {
new Intent(mContext, BinderProxyService.class),
connection,
Context.BIND_AUTO_CREATE);
- if (!bindLatch.await(500, TimeUnit.MILLISECONDS)) {
+ if (!bindLatch.await(1000, TimeUnit.MILLISECONDS)) {
fail(
"Timed out while binding service: "
+ BinderProxyService.class.getSimpleName());
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
deleted file mode 100644
index 30f6636766d5..000000000000
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2007 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.os;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-import androidx.test.filters.Suppress;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class MessageQueueTest {
-
-}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index d0f9a38720bf..419c05f5b8c1 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -49,9 +49,14 @@ public class MotionEventTest {
private static final int ID_SOURCE_MASK = 0x3 << 30;
private PointerCoords pointerCoords(float x, float y) {
+ return pointerCoords(x, y, 0f /*orientation*/);
+ }
+
+ private PointerCoords pointerCoords(float x, float y, float orientation) {
final var coords = new PointerCoords();
coords.x = x;
coords.y = y;
+ coords.orientation = orientation;
return coords;
}
@@ -295,4 +300,24 @@ public class MotionEventTest {
// Expected
}
}
+
+ @Test
+ public void testAxesAreNotAffectedByFlagChanges() {
+ final int pointerCount = 1;
+ final var properties = new PointerProperties[]{fingerProperties(0)};
+ final var coords = new PointerCoords[]{pointerCoords(20, 60, 0.1234f)};
+
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */,
+ 0 /* eventTime */, MotionEvent.ACTION_MOVE, pointerCount, properties, coords,
+ 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */, 1 /* yPrecision */,
+ 0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_TOUCHSCREEN,
+ 0 /* flags */);
+
+ // Mark the event as tainted to update the MotionEvent flags.
+ event.setTainted(true);
+
+ assertEquals(20f, event.getX(), 0.0001);
+ assertEquals(60f, event.getY(), 0.0001);
+ assertEquals(0.1234f, event.getOrientation(), 0.0001);
+ }
}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index bbdcbc9147e4..688bf83224aa 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -1,4 +1,6 @@
# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+# proto-message: flag_declarations
+# Project link: http://gantry/projects/android_platform_multitasking
package: "com.android.wm.shell"
container: "system"
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 239acd37f286..ab2e552c7a3d 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -49,7 +49,9 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
+import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
@@ -175,6 +177,137 @@ class BubbleStackViewTest {
}
@Test
+ fun expandStack_imeHidden() {
+ val bubble = createAndInflateBubble()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble)
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+ positioner.setImeVisible(false, 0)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // simulate a request from the bubble data listener to expand the stack
+ bubbleStackView.isExpanded = true
+ verify(sysuiProxy).onStackExpandChanged(true)
+ shellExecutor.flushAll()
+ }
+
+ assertThat(bubbleStackViewManager.onImeHidden).isNull()
+ }
+
+ @Test
+ fun collapseStack_imeHidden() {
+ val bubble = createAndInflateBubble()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble)
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+ positioner.setImeVisible(false, 0)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // simulate a request from the bubble data listener to expand the stack
+ bubbleStackView.isExpanded = true
+ verify(sysuiProxy).onStackExpandChanged(true)
+ shellExecutor.flushAll()
+ }
+
+ assertThat(bubbleStackViewManager.onImeHidden).isNull()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // simulate a request from the bubble data listener to collapse the stack
+ bubbleStackView.isExpanded = false
+ verify(sysuiProxy).onStackExpandChanged(false)
+ shellExecutor.flushAll()
+ }
+
+ assertThat(bubbleStackViewManager.onImeHidden).isNull()
+ }
+
+ @Test
+ fun expandStack_waitsForIme() {
+ val bubble = createAndInflateBubble()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble)
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+ positioner.setImeVisible(true, 100)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // simulate a request from the bubble data listener to expand the stack
+ bubbleStackView.isExpanded = true
+ }
+
+ val onImeHidden = bubbleStackViewManager.onImeHidden
+ assertThat(onImeHidden).isNotNull()
+ verify(sysuiProxy, never()).onStackExpandChanged(any())
+ positioner.setImeVisible(false, 0)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ onImeHidden!!.run()
+ verify(sysuiProxy).onStackExpandChanged(true)
+ shellExecutor.flushAll()
+ }
+ }
+
+ @Test
+ fun collapseStack_waitsForIme() {
+ val bubble = createAndInflateBubble()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble)
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+ positioner.setImeVisible(true, 100)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // simulate a request from the bubble data listener to expand the stack
+ bubbleStackView.isExpanded = true
+ }
+
+ var onImeHidden = bubbleStackViewManager.onImeHidden
+ assertThat(onImeHidden).isNotNull()
+ verify(sysuiProxy, never()).onStackExpandChanged(any())
+ positioner.setImeVisible(false, 0)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ onImeHidden!!.run()
+ verify(sysuiProxy).onStackExpandChanged(true)
+ shellExecutor.flushAll()
+ }
+
+ bubbleStackViewManager.onImeHidden = null
+ positioner.setImeVisible(true, 100)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // simulate a request from the bubble data listener to collapse the stack
+ bubbleStackView.isExpanded = false
+ }
+
+ onImeHidden = bubbleStackViewManager.onImeHidden
+ assertThat(onImeHidden).isNotNull()
+ verify(sysuiProxy, never()).onStackExpandChanged(false)
+ positioner.setImeVisible(false, 0)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ onImeHidden!!.run()
+ verify(sysuiProxy).onStackExpandChanged(false)
+ shellExecutor.flushAll()
+ }
+ }
+
+ @Test
fun tapDifferentBubble_shouldReorder() {
val bubble1 = createAndInflateChatBubble(key = "bubble1")
val bubble2 = createAndInflateChatBubble(key = "bubble2")
@@ -418,6 +551,7 @@ class BubbleStackViewTest {
}
private class FakeBubbleStackViewManager : BubbleStackViewManager {
+ var onImeHidden: Runnable? = null
override fun onAllBubblesAnimatedOut() {}
@@ -425,6 +559,8 @@ class BubbleStackViewTest {
override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
- override fun hideCurrentInputMethod() {}
+ override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
+ this.onImeHidden = onImeHidden
+ }
}
}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
index fcf74e3c1936..87c520ca1b51 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
@@ -48,7 +48,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxWidth="130dp"
- android:textAppearance="@android:style/TextAppearance.Material.Title"
android:textSize="14sp"
android:textFontWeight="500"
android:lineHeight="20sp"
@@ -56,9 +55,7 @@
android:layout_weight="1"
android:layout_marginStart="8dp"
android:singleLine="true"
- android:ellipsize="none"
- android:requiresFadingEdge="horizontal"
- android:fadingEdgeLength="28dp"
+ android:ellipsize="end"
android:clickable="false"
android:focusable="false"
tools:text="Gmail"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 8efeecb56dbf..08e36927d978 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -215,6 +215,8 @@ public class BubbleController implements ConfigurationChangeListener,
private final BubblePositioner mBubblePositioner;
private Bubbles.SysuiProxy mSysuiProxy;
+ @Nullable private Runnable mOnImeHidden;
+
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
// Current profiles of the user (e.g. user with a workprofile)
@@ -615,7 +617,8 @@ public class BubbleController implements ConfigurationChangeListener,
/**
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
*/
- void hideCurrentInputMethod() {
+ void hideCurrentInputMethod(@Nullable Runnable onImeHidden) {
+ mOnImeHidden = onImeHidden;
mBubblePositioner.setImeVisible(false /* visible */, 0 /* height */);
int displayId = mWindowManager.getDefaultDisplay().getDisplayId();
try {
@@ -2267,7 +2270,7 @@ public class BubbleController implements ConfigurationChangeListener,
if (mLayerView != null && mLayerView.isExpanded()) {
if (mBubblePositioner.isImeVisible()) {
// If we're collapsing, hide the IME
- hideCurrentInputMethod();
+ hideCurrentInputMethod(null);
}
mLayerView.collapse();
}
@@ -2552,6 +2555,10 @@ public class BubbleController implements ConfigurationChangeListener,
mBubblePositioner.setImeVisible(imeVisible, totalImeHeight);
if (mStackView != null) {
mStackView.setImeVisible(imeVisible);
+ if (!imeVisible && mOnImeHidden != null) {
+ mOnImeHidden.run();
+ mOnImeHidden = null;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index 6423eed59165..a02623138f1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -82,7 +82,7 @@ interface BubbleExpandedViewManager {
override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar
override fun hideCurrentInputMethod() {
- controller.hideCurrentInputMethod()
+ controller.hideCurrentInputMethod(null)
}
override fun updateBubbleBarLocation(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 249a218d5e56..979d958e3c5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -280,6 +280,7 @@ public class BubbleStackView extends FrameLayout
private int mCornerRadius;
@Nullable private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded;
+ private boolean mIsImeVisible = false;
/** Whether the stack is currently on the left side of the screen, or animating there. */
private boolean mStackOnLeftOrWillBe = true;
@@ -2233,29 +2234,40 @@ public class BubbleStackView extends FrameLayout
boolean wasExpanded = mIsExpanded;
- hideCurrentInputMethod();
+ // Do the actual expansion/collapse after the IME is hidden if it's currently visible in
+ // order to avoid flickers
+ Runnable onImeHidden = () -> {
+ mSysuiProxyProvider.getSysuiProxy().onStackExpandChanged(shouldExpand);
- mSysuiProxyProvider.getSysuiProxy().onStackExpandChanged(shouldExpand);
+ if (wasExpanded) {
+ stopMonitoringSwipeUpGesture();
+ animateCollapse();
+ showManageMenu(false);
+ logBubbleEvent(mExpandedBubble,
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ } else {
+ animateExpansion();
+ // TODO: move next line to BubbleData
+ logBubbleEvent(mExpandedBubble,
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+ logBubbleEvent(mExpandedBubble,
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
+ mManager.checkNotificationPanelExpandedState(notifPanelExpanded -> {
+ if (!notifPanelExpanded && mIsExpanded) {
+ startMonitoringSwipeUpGesture();
+ }
+ });
+ }
+ notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+ announceExpandForAccessibility(mExpandedBubble, mIsExpanded);
+ };
- if (wasExpanded) {
- stopMonitoringSwipeUpGesture();
- animateCollapse();
- showManageMenu(false);
- logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ if (mPositioner.isImeVisible()) {
+ hideCurrentInputMethod(onImeHidden);
} else {
- animateExpansion();
- // TODO: move next line to BubbleData
- logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
- logBubbleEvent(mExpandedBubble,
- FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
- mManager.checkNotificationPanelExpandedState(notifPanelExpanded -> {
- if (!notifPanelExpanded && mIsExpanded) {
- startMonitoringSwipeUpGesture();
- }
- });
+ // the IME is already hidden, so run the runnable immediately
+ onImeHidden.run();
}
- notifyExpansionChanged(mExpandedBubble, mIsExpanded);
- announceExpandForAccessibility(mExpandedBubble, mIsExpanded);
}
/**
@@ -2373,7 +2385,17 @@ public class BubbleStackView extends FrameLayout
* not.
*/
void hideCurrentInputMethod() {
- mManager.hideCurrentInputMethod();
+ mManager.hideCurrentInputMethod(null);
+ }
+
+ /**
+ * Hides the IME similar to {@link #hideCurrentInputMethod()} but also runs {@code onImeHidden}
+ * after after the IME is hidden.
+ *
+ * @see #hideCurrentInputMethod()
+ */
+ void hideCurrentInputMethod(Runnable onImeHidden) {
+ mManager.hideCurrentInputMethod(onImeHidden);
}
/** Set the stack position to whatever the positioner says. */
@@ -2865,6 +2887,10 @@ public class BubbleStackView extends FrameLayout
* and clip the expanded view.
*/
public void setImeVisible(boolean visible) {
+ if (mIsImeVisible == visible) {
+ return;
+ }
+ mIsImeVisible = visible;
if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
// This will update the animation so the bubbles move to position for the IME
mExpandedAnimationController.expandFromStack(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt
index fb597a05663b..8b901a1273f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt
@@ -34,7 +34,7 @@ interface BubbleStackViewManager {
fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>)
/** Requests to hide the current input method. */
- fun hideCurrentInputMethod()
+ fun hideCurrentInputMethod(onImeHidden: Runnable?)
companion object {
@@ -52,8 +52,8 @@ interface BubbleStackViewManager {
controller.isNotificationPanelExpanded(callback)
}
- override fun hideCurrentInputMethod() {
- controller.hideCurrentInputMethod()
+ override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
+ controller.hideCurrentInputMethod(onImeHidden)
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java
new file mode 100644
index 000000000000..3f76fd0220ff
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java
@@ -0,0 +1,324 @@
+/*
+ * 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.wm.shell.common.split;
+
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_DISMISSING;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_NONE;
+import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR;
+import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+/**
+ * This class governs how and when parallax and dimming effects are applied to task surfaces,
+ * usually when the divider is being moved around by the user (or during an animation).
+ */
+class ResizingEffectPolicy {
+ private final SplitLayout mSplitLayout;
+ /** The parallax algorithm we are currently using. */
+ private final int mParallaxType;
+
+ int mShrinkSide = DOCKED_INVALID;
+
+ // The current dismissing side.
+ int mDismissingSide = DOCKED_INVALID;
+
+ /**
+ * A {@link Point} that stores a single x and y value, representing the parallax translation
+ * we use on the app that the divider is moving toward. The app is either shrinking in size or
+ * getting pushed off the screen.
+ */
+ final Point mRetreatingSideParallax = new Point();
+ /**
+ * A {@link Point} that stores a single x and y value, representing the parallax translation
+ * we use on the app that the divider is moving away from. The app is either growing in size or
+ * getting pulled onto the screen.
+ */
+ final Point mAdvancingSideParallax = new Point();
+
+ // The dimming value to hint the dismissing side and progress.
+ float mDismissingDimValue = 0.0f;
+
+ /**
+ * Content bounds for the app that the divider is moving toward. This is the content that is
+ * currently drawn at the start of the divider movement. It stays unchanged throughout the
+ * divider's movement.
+ */
+ final Rect mRetreatingContent = new Rect();
+ /**
+ * Surface bounds for the app that the divider is moving toward. This is the "canvas" on
+ * which an app could potentially be drawn. It changes on every frame as the divider moves
+ * around.
+ */
+ final Rect mRetreatingSurface = new Rect();
+ /**
+ * Content bounds for the app that the divider is moving away from. This is the content that
+ * is currently drawn at the start of the divider movement. It stays unchanged throughout
+ * the divider's movement.
+ */
+ final Rect mAdvancingContent = new Rect();
+ /**
+ * Surface bounds for the app that the divider is moving away from. This is the "canvas" on
+ * which an app could potentially be drawn. It changes on every frame as the divider moves
+ * around.
+ */
+ final Rect mAdvancingSurface = new Rect();
+
+ final Rect mTempRect = new Rect();
+ final Rect mTempRect2 = new Rect();
+
+ ResizingEffectPolicy(int parallaxType, SplitLayout splitLayout) {
+ mParallaxType = parallaxType;
+ mSplitLayout = splitLayout;
+ }
+
+ /**
+ * Calculates the desired parallax values and stores them in {@link #mRetreatingSideParallax}
+ * and {@link #mAdvancingSideParallax}. These values will be then be applied in
+ * {@link #adjustRootSurface}.
+ *
+ * @param position The divider's position on the screen (x-coordinate in left-right split,
+ * y-coordinate in top-bottom split).
+ */
+ void applyDividerPosition(
+ int position, boolean isLeftRightSplit, DividerSnapAlgorithm snapAlgorithm) {
+ mDismissingSide = DOCKED_INVALID;
+ mRetreatingSideParallax.set(0, 0);
+ mAdvancingSideParallax.set(0, 0);
+ mDismissingDimValue = 0;
+ Rect displayBounds = mSplitLayout.getRootBounds();
+
+ int totalDismissingDistance = 0;
+ if (position < snapAlgorithm.getFirstSplitTarget().position) {
+ mDismissingSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
+ totalDismissingDistance = snapAlgorithm.getDismissStartTarget().position
+ - snapAlgorithm.getFirstSplitTarget().position;
+ } else if (position > snapAlgorithm.getLastSplitTarget().position) {
+ mDismissingSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
+ totalDismissingDistance = snapAlgorithm.getLastSplitTarget().position
+ - snapAlgorithm.getDismissEndTarget().position;
+ }
+
+ final boolean topLeftShrink = isLeftRightSplit
+ ? position < mSplitLayout.getTopLeftContentBounds().right
+ : position < mSplitLayout.getTopLeftContentBounds().bottom;
+ if (topLeftShrink) {
+ mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
+ mRetreatingContent.set(mSplitLayout.getTopLeftContentBounds());
+ mRetreatingSurface.set(mSplitLayout.getTopLeftBounds());
+ mAdvancingContent.set(mSplitLayout.getBottomRightContentBounds());
+ mAdvancingSurface.set(mSplitLayout.getBottomRightBounds());
+ } else {
+ mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
+ mRetreatingContent.set(mSplitLayout.getBottomRightContentBounds());
+ mRetreatingSurface.set(mSplitLayout.getBottomRightBounds());
+ mAdvancingContent.set(mSplitLayout.getTopLeftContentBounds());
+ mAdvancingSurface.set(mSplitLayout.getTopLeftBounds());
+ }
+
+ if (mDismissingSide != DOCKED_INVALID) {
+ float fraction =
+ Math.max(0, Math.min(snapAlgorithm.calculateDismissingFraction(position), 1f));
+ mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
+ if (mParallaxType == PARALLAX_DISMISSING) {
+ fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+ if (isLeftRightSplit) {
+ mRetreatingSideParallax.x = (int) (fraction * totalDismissingDistance);
+ } else {
+ mRetreatingSideParallax.y = (int) (fraction * totalDismissingDistance);
+ }
+ }
+ }
+
+ if (mParallaxType == PARALLAX_ALIGN_CENTER) {
+ if (isLeftRightSplit) {
+ mRetreatingSideParallax.x =
+ (mRetreatingSurface.width() - mRetreatingContent.width()) / 2;
+ } else {
+ mRetreatingSideParallax.y =
+ (mRetreatingSurface.height() - mRetreatingContent.height()) / 2;
+ }
+ } else if (mParallaxType == PARALLAX_FLEX) {
+ // Whether an app is getting pushed offscreen by the divider.
+ boolean isRetreatingOffscreen = !displayBounds.contains(mRetreatingSurface);
+ // Whether an app was getting pulled onscreen at the beginning of the drag.
+ boolean advancingSideStartedOffscreen = !displayBounds.contains(mAdvancingContent);
+
+ // The simpler case when an app gets pushed offscreen (e.g. 50:50 -> 90:10)
+ if (isRetreatingOffscreen && !advancingSideStartedOffscreen) {
+ // On the left side, we use parallax to simulate the contents sticking to the
+ // divider. This is because surfaces naturally expand to the bottom and right,
+ // so when a surface's area expands, the contents stick to the left. This is
+ // correct behavior on the right-side surface, but not the left.
+ if (topLeftShrink) {
+ if (isLeftRightSplit) {
+ mRetreatingSideParallax.x =
+ mRetreatingSurface.width() - mRetreatingContent.width();
+ } else {
+ mRetreatingSideParallax.y =
+ mRetreatingSurface.height() - mRetreatingContent.height();
+ }
+ }
+ // All other cases (e.g. 10:90 -> 50:50, 10:90 -> 90:10, 10:90 -> dismiss)
+ } else {
+ mTempRect.set(mRetreatingSurface);
+ Point rootOffset = new Point();
+ // 10:90 -> 50:50, 10:90, or dismiss right
+ if (advancingSideStartedOffscreen) {
+ // We have to handle a complicated case here to keep the parallax smooth.
+ // When the divider crosses the 50% mark, the retreating-side app surface
+ // will start expanding offscreen. This is expected and unavoidable, but
+ // makes the parallax look disjointed. In order to preserve the illusion,
+ // we add another offset (rootOffset) to simulate the surface staying
+ // onscreen.
+ mTempRect.intersect(displayBounds);
+ if (mRetreatingSurface.left < displayBounds.left) {
+ rootOffset.x = displayBounds.left - mRetreatingSurface.left;
+ }
+ if (mRetreatingSurface.top < displayBounds.top) {
+ rootOffset.y = displayBounds.top - mRetreatingSurface.top;
+ }
+
+ // On the left side, we again have to simulate the contents sticking to the
+ // divider.
+ if (!topLeftShrink) {
+ if (isLeftRightSplit) {
+ mAdvancingSideParallax.x =
+ mAdvancingSurface.width() - mAdvancingContent.width();
+ } else {
+ mAdvancingSideParallax.y =
+ mAdvancingSurface.height() - mAdvancingContent.height();
+ }
+ }
+ }
+
+ // In all these cases, the shrinking app also receives a center parallax.
+ if (isLeftRightSplit) {
+ mRetreatingSideParallax.x = rootOffset.x
+ + ((mTempRect.width() - mRetreatingContent.width()) / 2);
+ } else {
+ mRetreatingSideParallax.y = rootOffset.y
+ + ((mTempRect.height() - mRetreatingContent.height()) / 2);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
+ * slowing down parallax effect
+ */
+ private float calculateParallaxDismissingFraction(float fraction, int dockSide) {
+ float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
+
+ // Less parallax at the top, just because.
+ if (dockSide == WindowManager.DOCKED_TOP) {
+ result /= 2f;
+ }
+ return result;
+ }
+
+ /** Applies the calculated parallax and dimming values to task surfaces. */
+ void adjustRootSurface(SurfaceControl.Transaction t,
+ SurfaceControl leash1, SurfaceControl leash2) {
+ SurfaceControl retreatingLeash = null;
+ SurfaceControl advancingLeash = null;
+
+ if (mParallaxType == PARALLAX_DISMISSING) {
+ switch (mDismissingSide) {
+ case DOCKED_TOP:
+ case DOCKED_LEFT:
+ retreatingLeash = leash1;
+ mTempRect.set(mSplitLayout.getTopLeftBounds());
+ advancingLeash = leash2;
+ mTempRect2.set(mSplitLayout.getBottomRightBounds());
+ break;
+ case DOCKED_BOTTOM:
+ case DOCKED_RIGHT:
+ retreatingLeash = leash2;
+ mTempRect.set(mSplitLayout.getBottomRightBounds());
+ advancingLeash = leash1;
+ mTempRect2.set(mSplitLayout.getTopLeftBounds());
+ break;
+ }
+ } else if (mParallaxType == PARALLAX_ALIGN_CENTER || mParallaxType == PARALLAX_FLEX) {
+ switch (mShrinkSide) {
+ case DOCKED_TOP:
+ case DOCKED_LEFT:
+ retreatingLeash = leash1;
+ mTempRect.set(mSplitLayout.getTopLeftBounds());
+ advancingLeash = leash2;
+ mTempRect2.set(mSplitLayout.getBottomRightBounds());
+ break;
+ case DOCKED_BOTTOM:
+ case DOCKED_RIGHT:
+ retreatingLeash = leash2;
+ mTempRect.set(mSplitLayout.getBottomRightBounds());
+ advancingLeash = leash1;
+ mTempRect2.set(mSplitLayout.getTopLeftBounds());
+ break;
+ }
+ }
+ if (mParallaxType != PARALLAX_NONE
+ && retreatingLeash != null && advancingLeash != null) {
+ t.setPosition(retreatingLeash, mTempRect.left + mRetreatingSideParallax.x,
+ mTempRect.top + mRetreatingSideParallax.y);
+ // Transform the screen-based split bounds to surface-based crop bounds.
+ mTempRect.offsetTo(-mRetreatingSideParallax.x, -mRetreatingSideParallax.y);
+ t.setWindowCrop(retreatingLeash, mTempRect);
+
+ t.setPosition(advancingLeash, mTempRect2.left + mAdvancingSideParallax.x,
+ mTempRect2.top + mAdvancingSideParallax.y);
+ // Transform the screen-based split bounds to surface-based crop bounds.
+ mTempRect2.offsetTo(-mAdvancingSideParallax.x, -mAdvancingSideParallax.y);
+ t.setWindowCrop(advancingLeash, mTempRect2);
+ }
+ }
+
+ void adjustDimSurface(SurfaceControl.Transaction t,
+ SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+ SurfaceControl targetDimLayer;
+ switch (mDismissingSide) {
+ case DOCKED_TOP:
+ case DOCKED_LEFT:
+ targetDimLayer = dimLayer1;
+ break;
+ case DOCKED_BOTTOM:
+ case DOCKED_RIGHT:
+ targetDimLayer = dimLayer2;
+ break;
+ case DOCKED_INVALID:
+ default:
+ t.setAlpha(dimLayer1, 0).hide(dimLayer1);
+ t.setAlpha(dimLayer2, 0).hide(dimLayer2);
+ return;
+ }
+ t.setAlpha(targetDimLayer, mDismissingDimValue)
+ .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 9fb36b36ff29..a73d43c54064 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -227,10 +227,37 @@ public class SplitDecorManager extends WindowlessWindowManager {
mInstantaneousBounds.setEmpty();
}
- /** Showing resizing hint. */
+ /**
+ * Called on every frame when an app is getting resized, and controls the showing & hiding of
+ * the app veil. IMPORTANT: There is one SplitDecorManager for each task, so if two tasks are
+ * getting resized simultaneously, this method is called in parallel on the other
+ * SplitDecorManager too. In general, we want to hide the app behind a veil when:
+ * a) the app is stretching past its original bounds (because app content layout doesn't
+ * update mid-stretch).
+ * b) the app is resizing down from fullscreen (because there is no parallax effect that
+ * makes every app look good in this scenario).
+ * In the world of flexible split, where apps can go offscreen, there is an exception to this:
+ * - We do NOT hide the app when it is going offscreen, even though it is technically
+ * getting larger and would qualify for condition (a). Instead, we use parallax to give
+ * the illusion that the app is getting pushed offscreen by the divider.
+ *
+ * @param resizingTask The task that is getting resized.
+ * @param newBounds The bounds that that we are updating this surface to. This can be an
+ * instantaneous bounds, just for a frame, during a drag or animation.
+ * @param sideBounds The bounds of the OPPOSITE task in the split layout. This is used just for
+ * reference/calculation, the surface of the other app won't be set here.
+ * @param displayBounds The bounds of the entire display.
+ * @param t The transaction on which these changes will be bundled.
+ * @param offsetX The x-translation applied to the task surface for parallax. Will be used to
+ * position the task screenshot and/or icon veil.
+ * @param offsetY The x-translation applied to the task surface for parallax. Will be used to
+ * position the task screenshot and/or icon veil.
+ * @param immediately {@code true} if the veil should transition in/out instantly, with no
+ * animation.
+ */
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
- Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY,
- boolean immediately) {
+ Rect sideBounds, Rect displayBounds, SurfaceControl.Transaction t, int offsetX,
+ int offsetY, boolean immediately) {
if (mVeilIconView == null) {
return;
}
@@ -252,7 +279,10 @@ public class SplitDecorManager extends WindowlessWindowManager {
final boolean isStretchingPastOriginalBounds =
newBounds.width() > mOldMainBounds.width()
|| newBounds.height() > mOldMainBounds.height();
- final boolean showVeil = isResizingDownFromFullscreen || isStretchingPastOriginalBounds;
+ final boolean isFullyOnscreen = displayBounds.contains(newBounds);
+ boolean showVeil = isFullyOnscreen
+ && (isResizingDownFromFullscreen || isStretchingPastOriginalBounds);
+
final boolean update = showVeil != mShown;
if (update && mFadeAnimator != null && mFadeAnimator.isRunning()) {
// If we need to animate and animator still running, cancel it before we ensure both
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 4bcec702281d..fc274d6e7174 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -18,19 +18,14 @@ package com.android.wm.shell.common.split;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
-import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR;
import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.wm.shell.shared.animation.Interpolators.LINEAR;
-import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45;
@@ -52,7 +47,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.util.Log;
@@ -103,9 +97,17 @@ import java.util.function.Consumer;
*/
public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
private static final String TAG = "SplitLayout";
+ /** No parallax effect when the user is dragging the divider */
public static final int PARALLAX_NONE = 0;
public static final int PARALLAX_DISMISSING = 1;
+ /** Parallax effect (center-aligned) when the user is dragging the divider */
public static final int PARALLAX_ALIGN_CENTER = 2;
+ /**
+ * A custom parallax effect for flexible split. When an app is being pushed/pulled offscreen,
+ * we use a specific parallax to give the impression that it is stuck to the divider.
+ * Otherwise, we fall back to PARALLAX_ALIGN_CENTER behavior.
+ */
+ public static final int PARALLAX_FLEX = 3;
public static final int FLING_RESIZE_DURATION = 250;
private static final int FLING_ENTER_DURATION = 450;
@@ -146,6 +148,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private int mDividerSize;
private final Rect mTempRect = new Rect();
+ private final Rect mTempRect2 = new Rect();
private final Rect mRootBounds = new Rect();
private final Rect mDividerBounds = new Rect();
/**
@@ -219,7 +222,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
- mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
+ mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType, this);
mSplitState = splitState;
final Resources res = mContext.getResources();
@@ -580,7 +583,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
if (setEffectBounds) {
- mSurfaceEffectPolicy.applyDividerPosition(position, mIsLeftRightSplit);
+ mSurfaceEffectPolicy.applyDividerPosition(
+ position, mIsLeftRightSplit, mDividerSnapAlgorithm);
}
}
@@ -710,8 +714,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
*/
void updateDividerBounds(int position, boolean shouldUseParallaxEffect) {
updateBounds(position);
- mSplitLayoutHandler.onLayoutSizeChanging(this, mSurfaceEffectPolicy.mParallaxOffset.x,
- mSurfaceEffectPolicy.mParallaxOffset.y, shouldUseParallaxEffect);
+ mSplitLayoutHandler.onLayoutSizeChanging(this,
+ mSurfaceEffectPolicy.mRetreatingSideParallax.x,
+ mSurfaceEffectPolicy.mRetreatingSideParallax.y, shouldUseParallaxEffect);
}
void setDividerPosition(int position, boolean applyLayoutChange) {
@@ -1361,169 +1366,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
int getSplitItemPosition(WindowContainerToken token);
}
- /**
- * Calculates and applies proper dismissing parallax offset and dimming value to hint users
- * dismissing gesture.
- */
- private class ResizingEffectPolicy {
- /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
- private final int mParallaxType;
-
- int mShrinkSide = DOCKED_INVALID;
-
- // The current dismissing side.
- int mDismissingSide = DOCKED_INVALID;
-
- // The parallax offset to hint the dismissing side and progress.
- final Point mParallaxOffset = new Point();
-
- // The dimming value to hint the dismissing side and progress.
- float mDismissingDimValue = 0.0f;
- final Rect mContentBounds = new Rect();
- final Rect mSurfaceBounds = new Rect();
-
- ResizingEffectPolicy(int parallaxType) {
- mParallaxType = parallaxType;
- }
-
- /**
- * Applies a parallax to the task to hint dismissing progress.
- *
- * @param position the split position to apply dismissing parallax effect
- * @param isLeftRightSplit indicates whether it's splitting horizontally or vertically
- */
- void applyDividerPosition(int position, boolean isLeftRightSplit) {
- mDismissingSide = DOCKED_INVALID;
- mParallaxOffset.set(0, 0);
- mDismissingDimValue = 0;
-
- int totalDismissingDistance = 0;
- if (position < mDividerSnapAlgorithm.getFirstSplitTarget().position) {
- mDismissingSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
- totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
- - mDividerSnapAlgorithm.getFirstSplitTarget().position;
- } else if (position > mDividerSnapAlgorithm.getLastSplitTarget().position) {
- mDismissingSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
- totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
- - mDividerSnapAlgorithm.getDismissEndTarget().position;
- }
-
- final boolean topLeftShrink = isLeftRightSplit
- ? position < getTopLeftContentBounds().right
- : position < getTopLeftContentBounds().bottom;
- if (topLeftShrink) {
- mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
- mContentBounds.set(getTopLeftContentBounds());
- mSurfaceBounds.set(getTopLeftBounds());
- } else {
- mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
- mContentBounds.set(getBottomRightContentBounds());
- mSurfaceBounds.set(getBottomRightBounds());
- }
-
- if (mDismissingSide != DOCKED_INVALID) {
- float fraction = Math.max(0,
- Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
- mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
- if (mParallaxType == PARALLAX_DISMISSING) {
- fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
- if (isLeftRightSplit) {
- mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
- } else {
- mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
- }
- }
- }
-
- if (mParallaxType == PARALLAX_ALIGN_CENTER) {
- if (isLeftRightSplit) {
- mParallaxOffset.x =
- (mSurfaceBounds.width() - mContentBounds.width()) / 2;
- } else {
- mParallaxOffset.y =
- (mSurfaceBounds.height() - mContentBounds.height()) / 2;
- }
- }
- }
-
- /**
- * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
- * slowing down parallax effect
- */
- private float calculateParallaxDismissingFraction(float fraction, int dockSide) {
- float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
-
- // Less parallax at the top, just because.
- if (dockSide == WindowManager.DOCKED_TOP) {
- result /= 2f;
- }
- return result;
- }
-
- /** Applies parallax offset and dimming value to the root surface at the dismissing side. */
- void adjustRootSurface(SurfaceControl.Transaction t,
- SurfaceControl leash1, SurfaceControl leash2) {
- SurfaceControl targetLeash = null;
-
- if (mParallaxType == PARALLAX_DISMISSING) {
- switch (mDismissingSide) {
- case DOCKED_TOP:
- case DOCKED_LEFT:
- targetLeash = leash1;
- mTempRect.set(getTopLeftBounds());
- break;
- case DOCKED_BOTTOM:
- case DOCKED_RIGHT:
- targetLeash = leash2;
- mTempRect.set(getBottomRightBounds());
- break;
- }
- } else if (mParallaxType == PARALLAX_ALIGN_CENTER) {
- switch (mShrinkSide) {
- case DOCKED_TOP:
- case DOCKED_LEFT:
- targetLeash = leash1;
- mTempRect.set(getTopLeftBounds());
- break;
- case DOCKED_BOTTOM:
- case DOCKED_RIGHT:
- targetLeash = leash2;
- mTempRect.set(getBottomRightBounds());
- break;
- }
- }
- if (mParallaxType != PARALLAX_NONE && targetLeash != null) {
- t.setPosition(targetLeash,
- mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y);
- // Transform the screen-based split bounds to surface-based crop bounds.
- mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y);
- t.setWindowCrop(targetLeash, mTempRect);
- }
- }
-
- void adjustDimSurface(SurfaceControl.Transaction t,
- SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
- SurfaceControl targetDimLayer;
- switch (mDismissingSide) {
- case DOCKED_TOP:
- case DOCKED_LEFT:
- targetDimLayer = dimLayer1;
- break;
- case DOCKED_BOTTOM:
- case DOCKED_RIGHT:
- targetDimLayer = dimLayer2;
- break;
- case DOCKED_INVALID:
- default:
- t.setAlpha(dimLayer1, 0).hide(dimLayer1);
- t.setAlpha(dimLayer2, 0).hide(dimLayer2);
- return;
- }
- t.setAlpha(targetDimLayer, mDismissingDimValue)
- .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
- }
- }
-
/** Records IME top offset changes and updates SplitLayout correspondingly. */
private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
index 9c951bd89876..6f1dc56e273a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
@@ -43,7 +43,7 @@ import java.util.Map;
*/
public class SplitSpec {
private static final String TAG = "SplitSpec";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
/** A split ratio used on larger screens, where we can fit both apps onscreen. */
public static final float ONSCREEN_ONLY_ASYMMETRIC_RATIO = 0.33f;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 535112f5124a..8ecf483a7e75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -35,7 +35,9 @@ import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static com.android.wm.shell.Flags.enableFlexibleSplit;
+import static com.android.wm.shell.Flags.enableFlexibleTwoAppSplit;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
@@ -127,7 +129,6 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.policy.FoldLockSettingsObserver;
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -2007,7 +2008,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// If all stages are filled, create new SplitBounds and update Recents.
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
int currentSnapPosition = mSplitLayout.calculateCurrentSnapPosition();
- if (Flags.enableFlexibleTwoAppSplit()) {
+ if (enableFlexibleTwoAppSplit()) {
// Split screen can be laid out in such a way that some of the apps are
// offscreen. For the purposes of passing SplitBounds up to launcher (for use in
// thumbnails etc.), we crop the bounds down to the screen size.
@@ -2064,10 +2065,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTaskLeash = leash;
if (mSplitLayout == null) {
+ int parallaxType = enableFlexibleTwoAppSplit() ? PARALLAX_FLEX : PARALLAX_ALIGN_CENTER;
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mRootTaskInfo.configuration, this, mParentContainerCallbacks,
- mDisplayController, mDisplayImeController, mTaskOrganizer,
- PARALLAX_ALIGN_CENTER /* parallaxType */, mSplitState, mMainHandler);
+ mDisplayController, mDisplayImeController, mTaskOrganizer, parallaxType,
+ mSplitState, mMainHandler);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
@@ -2407,6 +2409,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
updateSurfaceBounds(layout, t, shouldUseParallaxEffect);
getMainStageBounds(mTempRect1);
getSideStageBounds(mTempRect2);
+ Rect displayBounds = mSplitLayout.getRootBounds();
+
if (enableFlexibleSplit()) {
StageTaskListener ltStage =
mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT,
@@ -2414,12 +2418,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
StageTaskListener brStage =
mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT,
false /*checkAllStagesIfNotActive*/);
- ltStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately);
- brStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately);
+ ltStage.onResizing(mTempRect1, mTempRect2, displayBounds, t, offsetX, offsetY,
+ mShowDecorImmediately);
+ brStage.onResizing(mTempRect2, mTempRect1, displayBounds, t, offsetX, offsetY,
+ mShowDecorImmediately);
} else {
- mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY,
+ mMainStage.onResizing(mTempRect1, mTempRect2, displayBounds, t, offsetX, offsetY,
mShowDecorImmediately);
- mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY,
+ mSideStage.onResizing(mTempRect2, mTempRect1, displayBounds, t, offsetX, offsetY,
mShowDecorImmediately);
}
t.apply();
@@ -2466,7 +2472,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.populateTouchZones();
}, mainDecor, sideDecor, decorManagers);
- if (Flags.enableFlexibleTwoAppSplit()) {
+ if (enableFlexibleTwoAppSplit()) {
switch (layout.calculateCurrentSnapPosition()) {
case SNAP_TO_2_10_90 -> grantFocusToPosition(false /* leftOrTop */);
case SNAP_TO_2_90_10 -> grantFocusToPosition(true /* leftOrTop */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 816f51f997d5..29751986959b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -339,11 +339,11 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return mRootTaskInfo != null && mRootTaskInfo.taskId == taskId;
}
- void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX,
- int offsetY, boolean immediately) {
+ void onResizing(Rect newBounds, Rect sideBounds, Rect displayBounds,
+ SurfaceControl.Transaction t, int offsetX, int offsetY, boolean immediately) {
if (mSplitDecorManager != null && mRootTaskInfo != null) {
- mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX,
- offsetY, immediately);
+ mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, displayBounds, t,
+ offsetX, offsetY, immediately);
}
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index cc4a29b31996..12b1dd794a03 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -80,6 +80,7 @@ cc_library {
"LoadedArsc.cpp",
"Locale.cpp",
"LocaleData.cpp",
+ "LocaleDataLookup.cpp",
"misc.cpp",
"NinePatch.cpp",
"ObbFile.cpp",
@@ -225,6 +226,7 @@ cc_test {
"tests/Idmap_test.cpp",
"tests/LoadedArsc_test.cpp",
"tests/Locale_test.cpp",
+ "tests/LocaleDataLookup_test.cpp",
"tests/NinePatch_test.cpp",
"tests/ResourceTimer_test.cpp",
"tests/ResourceUtils_test.cpp",
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 020cef6012e9..1b23d90c5ab3 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -23,39 +23,18 @@
#include <unordered_set>
#include <androidfw/LocaleData.h>
+#include <androidfw/LocaleDataLookup.h>
namespace android {
-#include "LocaleDataTables.cpp"
-
-inline uint32_t packLocale(const char* language, const char* region) {
- return (((uint8_t) language[0]) << 24u) | (((uint8_t) language[1]) << 16u) |
- (((uint8_t) region[0]) << 8u) | ((uint8_t) region[1]);
-}
-
-inline uint32_t dropRegion(uint32_t packed_locale) {
- return packed_locale & 0xFFFF0000LU;
-}
-
-inline bool hasRegion(uint32_t packed_locale) {
- return (packed_locale & 0x0000FFFFLU) != 0;
-}
-
-const size_t SCRIPT_LENGTH = 4;
-const size_t SCRIPT_PARENTS_COUNT = sizeof(SCRIPT_PARENTS)/sizeof(SCRIPT_PARENTS[0]);
const uint32_t PACKED_ROOT = 0; // to represent the root locale
+const uint32_t MAX_PARENT_DEPTH = getMaxAncestorTreeDepth();
uint32_t findParent(uint32_t packed_locale, const char* script) {
if (hasRegion(packed_locale)) {
- for (size_t i = 0; i < SCRIPT_PARENTS_COUNT; i++) {
- if (memcmp(script, SCRIPT_PARENTS[i].script, SCRIPT_LENGTH) == 0) {
- auto map = SCRIPT_PARENTS[i].map;
- auto lookup_result = map->find(packed_locale);
- if (lookup_result != map->end()) {
- return lookup_result->second;
- }
- break;
- }
+ auto parent_key = findParentLocalePackedKey(script, packed_locale);
+ if (parent_key != 0) {
+ return parent_key;
}
return dropRegion(packed_locale);
}
@@ -111,17 +90,6 @@ size_t findDistance(uint32_t supported,
return supported_ancestor_count + request_ancestors_index - 1;
}
-inline bool isRepresentative(uint32_t language_and_region, const char* script) {
- const uint64_t packed_locale = (
- (((uint64_t) language_and_region) << 32u) |
- (((uint64_t) script[0]) << 24u) |
- (((uint64_t) script[1]) << 16u) |
- (((uint64_t) script[2]) << 8u) |
- ((uint64_t) script[3]));
-
- return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
-}
-
const uint32_t US_SPANISH = 0x65735553LU; // es-US
const uint32_t MEXICAN_SPANISH = 0x65734D58LU; // es-MX
const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424LU; // es-419
@@ -185,8 +153,8 @@ int localeDataCompareRegions(
// If we are here, left and right are equidistant from the request. We will
// try and see if any of them is a representative locale.
- const bool left_is_representative = isRepresentative(left, requested_script);
- const bool right_is_representative = isRepresentative(right, requested_script);
+ const bool left_is_representative = isLocaleRepresentative(left, requested_script);
+ const bool right_is_representative = isLocaleRepresentative(right, requested_script);
if (left_is_representative != right_is_representative) {
return (int) left_is_representative - (int) right_is_representative;
}
@@ -204,14 +172,14 @@ void localeDataComputeScript(char out[4], const char* language, const char* regi
return;
}
uint32_t lookup_key = packLocale(language, region);
- auto lookup_result = LIKELY_SCRIPTS.find(lookup_key);
- if (lookup_result == LIKELY_SCRIPTS.end()) {
+ auto lookup_result = lookupLikelyScript(lookup_key);
+ if (lookup_result == nullptr) {
// We couldn't find the locale. Let's try without the region
if (region[0] != '\0') {
lookup_key = dropRegion(lookup_key);
- lookup_result = LIKELY_SCRIPTS.find(lookup_key);
- if (lookup_result != LIKELY_SCRIPTS.end()) {
- memcpy(out, SCRIPT_CODES[lookup_result->second], SCRIPT_LENGTH);
+ lookup_result = lookupLikelyScript(lookup_key);
+ if (lookup_result != nullptr) {
+ memcpy(out, lookup_result, SCRIPT_LENGTH);
return;
}
}
@@ -220,7 +188,7 @@ void localeDataComputeScript(char out[4], const char* language, const char* regi
return;
} else {
// We found the locale.
- memcpy(out, SCRIPT_CODES[lookup_result->second], SCRIPT_LENGTH);
+ memcpy(out, lookup_result, SCRIPT_LENGTH);
}
}
diff --git a/libs/androidfw/LocaleDataLookup.cpp b/libs/androidfw/LocaleDataLookup.cpp
new file mode 100644
index 000000000000..5441e2258900
--- /dev/null
+++ b/libs/androidfw/LocaleDataLookup.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include <androidfw/LocaleDataLookup.h>
+
+namespace android {
+
+#include "LocaleDataTables.cpp"
+
+const size_t SCRIPT_PARENTS_COUNT = sizeof(SCRIPT_PARENTS)/sizeof(SCRIPT_PARENTS[0]);
+
+const char* lookupLikelyScript(uint32_t packed_lang_region) {
+
+ auto lookup_result = LIKELY_SCRIPTS.find(packed_lang_region);
+ if (lookup_result == LIKELY_SCRIPTS.end()) {
+ return nullptr;
+ } else {
+ return SCRIPT_CODES[lookup_result->second];
+ }
+}
+
+uint32_t findParentLocalePackedKey(const char* script, uint32_t packed_lang_region) {
+ for (size_t i = 0; i < SCRIPT_PARENTS_COUNT; i++) {
+ if (memcmp(script, SCRIPT_PARENTS[i].script, SCRIPT_LENGTH) == 0) {
+ auto map = SCRIPT_PARENTS[i].map;
+ auto lookup_result = map->find(packed_lang_region);
+ if (lookup_result != map->end()) {
+ return lookup_result->second;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+uint32_t getMaxAncestorTreeDepth() {
+ return MAX_PARENT_DEPTH;
+}
+
+namespace hidden {
+
+bool isRepresentative(uint64_t packed_locale) {
+ return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
+}
+
+} // namespace hidden
+
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/LocaleDataLookup.h b/libs/androidfw/include/androidfw/LocaleDataLookup.h
new file mode 100644
index 000000000000..7fde7123ed0b
--- /dev/null
+++ b/libs/androidfw/include/androidfw/LocaleDataLookup.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+namespace android {
+
+namespace hidden {
+ bool isRepresentative(uint64_t packed_locale);
+}
+
+constexpr size_t SCRIPT_LENGTH = 4;
+
+constexpr inline uint32_t packLocale(const char* language, const char* region) {
+ const unsigned char* lang = reinterpret_cast<const unsigned char*>(language);
+ const unsigned char* reg = reinterpret_cast<const unsigned char*>(region);
+ return (static_cast<uint32_t>(lang[0]) << 24u) |
+ (static_cast<uint32_t>(lang[1]) << 16u) |
+ (static_cast<uint32_t>(reg[0]) << 8u) |
+ static_cast<uint32_t>(reg[1]);
+}
+
+constexpr inline uint32_t dropRegion(uint32_t packed_locale) {
+ return packed_locale & 0xFFFF0000LU;
+}
+
+constexpr inline bool hasRegion(uint32_t packed_locale) {
+ return (packed_locale & 0x0000FFFFLU) != 0;
+}
+
+/**
+ * Return nullptr if the key isn't found. The input packed_lang_region can be computed
+ * by android::packLocale.
+ * Note that the returned char* is either nullptr or 4-byte char seqeuence, but isn't
+ * a null-terminated string.
+ */
+const char* lookupLikelyScript(uint32_t packed_lang_region);
+/**
+ * Return false if the key isn't representative. The input lookup key can be computed
+ * by android::packLocale.
+ */
+bool inline isLocaleRepresentative(uint32_t language_and_region, const char* script) {
+ const unsigned char* s = reinterpret_cast<const unsigned char*>(script);
+ const uint64_t packed_locale = (
+ ((static_cast<uint64_t>(language_and_region)) << 32u) |
+ (static_cast<uint64_t>(s[0]) << 24u) |
+ (static_cast<uint64_t>(s[1]) << 16u) |
+ (static_cast<uint64_t>(s[2]) << 8u) |
+ static_cast<uint64_t>(s[3]));
+
+ return hidden::isRepresentative(packed_locale);
+}
+
+/**
+ * Return a parent packed key for a given script and child packed key. Return 0 if
+ * no parent is found.
+ */
+uint32_t findParentLocalePackedKey(const char* script, uint32_t packed_lang_region);
+
+uint32_t getMaxAncestorTreeDepth();
+
+} // namespace android
diff --git a/libs/androidfw/tests/LocaleDataLookup_test.cpp b/libs/androidfw/tests/LocaleDataLookup_test.cpp
new file mode 100644
index 000000000000..26b220d63169
--- /dev/null
+++ b/libs/androidfw/tests/LocaleDataLookup_test.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include "androidfw/LocaleDataLookup.h"
+
+#include <cstddef>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+
+namespace android {
+
+constexpr const char NULL_SCRIPT[4] = {'\0', '\0', '\0','\0' };
+
+#define EXPECT_SCEIPT_EQ(ex, s) EXPECT_EQ(0, s == nullptr ? -1 : memcmp(ex, s, 4))
+
+// Similar to packLanguageOrRegion() in ResourceTypes.cpp
+static uint32_t encodeLanguageOrRegionLiteral(const char* in, const char base) {
+ size_t len = strlen(in);
+ if (len <= 1) {
+ return 0;
+ }
+
+ if (len == 2) {
+ return (((uint8_t) in[0]) << 8) | ((uint8_t) in[1]);
+ }
+ uint8_t first = (in[0] - base) & 0x007f;
+ uint8_t second = (in[1] - base) & 0x007f;
+ uint8_t third = (in[2] - base) & 0x007f;
+
+ return ((uint8_t) (0x80 | (third << 2) | (second >> 3)) << 8) | ((second << 5) | first);
+}
+
+static uint32_t encodeLocale(const char* language, const char* region) {
+ return (encodeLanguageOrRegionLiteral(language, 'a') << 16) |
+ encodeLanguageOrRegionLiteral(region, '0');
+}
+
+TEST(LocaleDataLookupTest, lookupLikelyScript) {
+ EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("", "")));
+ EXPECT_SCEIPT_EQ("Latn", lookupLikelyScript(encodeLocale("en", "")));
+ EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("en", "US")));
+ EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("en", "GB")));
+ EXPECT_SCEIPT_EQ("Latn", lookupLikelyScript(encodeLocale("fr", "")));
+ EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("fr", "FR")));
+
+
+ EXPECT_SCEIPT_EQ("~~~A", lookupLikelyScript(encodeLocale("en", "XA")));
+ EXPECT_SCEIPT_EQ("Latn", lookupLikelyScript(encodeLocale("ha", "")));
+ EXPECT_SCEIPT_EQ("Arab", lookupLikelyScript(encodeLocale("ha", "SD")));
+ EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("ha", "Sd"))); // case sensitive
+ EXPECT_SCEIPT_EQ("Hans", lookupLikelyScript(encodeLocale("zh", "")));
+ EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("zh", "CN")));
+ EXPECT_SCEIPT_EQ("Hant", lookupLikelyScript(encodeLocale("zh", "HK")));
+
+ EXPECT_SCEIPT_EQ("Nshu", lookupLikelyScript(encodeLocale("zhx", "")));
+ EXPECT_SCEIPT_EQ("Nshu", lookupLikelyScript(0xDCF90000u)); // encoded "zhx"
+}
+
+TEST(LocaleDataLookupTest, isLocaleRepresentative) {
+ EXPECT_TRUE(isLocaleRepresentative(encodeLocale("en", "US"), "Latn"));
+ EXPECT_TRUE(isLocaleRepresentative(encodeLocale("en", "GB"), "Latn"));
+ EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", "US"), NULL_SCRIPT));
+ EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", ""), "Latn"));
+ EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", ""), NULL_SCRIPT));
+ EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", "US"), "Arab"));
+
+ EXPECT_TRUE(isLocaleRepresentative(encodeLocale("fr", "FR"), "Latn"));
+
+ EXPECT_TRUE(isLocaleRepresentative(encodeLocale("zh", "CN"), "Hans"));
+ EXPECT_FALSE(isLocaleRepresentative(encodeLocale("zh", "TW"), "Hans"));
+ EXPECT_FALSE(isLocaleRepresentative(encodeLocale("zhx", "CN"), "Hans"));
+ EXPECT_FALSE(isLocaleRepresentative(0xDCF9434E, "Hans"));
+ EXPECT_TRUE(isLocaleRepresentative(encodeLocale("zhx", "CN"), "Nshu"));
+ EXPECT_TRUE(isLocaleRepresentative(0xDCF9434E, "Nshu"));
+}
+
+TEST(LocaleDataLookupTest, findParentLocalePackedKey) {
+ EXPECT_EQ(encodeLocale("en", "001"), findParentLocalePackedKey("Latn", encodeLocale("en", "GB")));
+ EXPECT_EQ(0x656E8400u, findParentLocalePackedKey("Latn", encodeLocale("en", "GB")));
+
+ EXPECT_EQ(encodeLocale("en", "IN"), findParentLocalePackedKey("Deva", encodeLocale("hi", "")));
+
+ EXPECT_EQ(encodeLocale("ar", "015"), findParentLocalePackedKey("Arab", encodeLocale("ar", "AE")));
+ EXPECT_EQ(0x61729420u, findParentLocalePackedKey("Arab", encodeLocale("ar", "AE")));
+
+ EXPECT_EQ(encodeLocale("ar", "015"), findParentLocalePackedKey("~~~B", encodeLocale("ar", "XB")));
+ EXPECT_EQ(0x61729420u, findParentLocalePackedKey("Arab", encodeLocale("ar", "AE")));
+
+ EXPECT_EQ(encodeLocale("zh", "HK"), findParentLocalePackedKey("Hant", encodeLocale("zh", "MO")));
+}
+
+} // namespace android
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f1f03c31f718..ed193515b382 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2470,7 +2470,7 @@ public class SettingsProvider extends ContentProvider {
boolean isRestrictedShell = android.security.Flags.protectDeviceConfigFlags()
&& hasAllowlistPermission;
- if (!isRestrictedShell && hasWritePermission) {
+ if (hasWritePermission) {
assertCallingUserDenyList(flags);
} else if (hasAllowlistPermission) {
Set<String> allowlistedDeviceConfigNamespaces = null;
@@ -2500,7 +2500,7 @@ public class SettingsProvider extends ContentProvider {
}
if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
- Slog.wtf(LOG_TAG, "Permission denial for flag '" + flag
+ throw new SecurityException("Permission denial for flag '" + flag
+ "'; allowlist permission granted, but must add flag to the "
+ "allowlist");
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index a945c33bc20a..9355bf8e4142 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -634,7 +634,7 @@ public class SettingsProviderTest extends BaseSettingsProviderTest {
throws Exception {
setSettingAndAssertSuccessfulChange(() -> {
insertStringViaProviderApi(type, name, value, withTableRowUri);
- }, type, name, value, UserHandle.USER_SYSTEM);
+ }, type, name, value, getContext().getUserId());
}
private void setSettingAndAssertSuccessfulChange(Runnable setCommand, final int type,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c6555041164d..61f49db07abc 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -2419,8 +2419,8 @@ public class BugreportProgressService extends Service {
bugreportLocationInfo = new BugreportLocationInfo(readFile(in));
int screenshotSize = in.readInt();
+ screenshotLocationInfo = new ScreenshotLocationInfo(null);
for (int i = 1; i <= screenshotSize; i++) {
- screenshotLocationInfo = new ScreenshotLocationInfo(null);
screenshotLocationInfo.mScreenshotFiles.add(readFile(in));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
index e00f6a6d6418..e94f04f8b35c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
@@ -152,8 +152,8 @@ public class AccessibilityTargetAdapterTest extends SysuiTestCase {
@EnableFlags(
com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
public void setBadgeOnLeftSide_false_rightBadgeVisibleAndLeftBadgeInvisible() {
- when(mAccessibilityTarget.getId()).thenReturn(
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ when(mAccessibilityTarget.getId())
+ .thenReturn(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mAdapter.setBadgeOnLeftSide(false);
mAdapter.onBindViewHolder(mViewHolder, 0);
@@ -166,8 +166,8 @@ public class AccessibilityTargetAdapterTest extends SysuiTestCase {
@EnableFlags(
com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
public void setBadgeOnLeftSide_rightBadgeInvisibleAndLeftBadgeVisible() {
- when(mAccessibilityTarget.getId()).thenReturn(
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ when(mAccessibilityTarget.getId())
+ .thenReturn(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mAdapter.setBadgeOnLeftSide(true);
mAdapter.onBindViewHolder(mViewHolder, 0);
@@ -180,8 +180,8 @@ public class AccessibilityTargetAdapterTest extends SysuiTestCase {
@EnableFlags(
com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
public void setBadgeOnLeftSide_bindViewHolderPayloads_rightBadgeInvisibleAndLeftBadgeVisible() {
- when(mAccessibilityTarget.getId()).thenReturn(
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ when(mAccessibilityTarget.getId())
+ .thenReturn(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mAdapter.setBadgeOnLeftSide(true);
mAdapter.onBindViewHolder(mViewHolder, 0, List.of(PAYLOAD_HEARING_STATUS_DRAWABLE));
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index 25d78e3474d5..b527506e28f5 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -17,6 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <corners android:radius="@dimen/volume_dialog_background_corner_radius" />
+ <corners android:bottomLeftRadius="@dimen/volume_dialog_background_corner_radius"
+ android:bottomRightRadius="@dimen/volume_dialog_background_corner_radius"/>
<solid android:color="@androidprv:color/materialColorSurface" />
</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml b/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
index 2694435bcc78..d7607cc17f2a 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
@@ -17,5 +17,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="20dp" />
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="@androidprv:color/materialColorSurface" />
</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml b/packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml
new file mode 100644
index 000000000000..6a706f739267
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/volume_dialog_background_corner_radius"/>
+ <solid android:color="@androidprv:color/materialColorSurface" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 327075967dba..0ec668012cfc 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -19,6 +19,7 @@
android:id="@+id/volume_dialog_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipChildren="false"
app:layoutDescription="@xml/volume_dialog_scene">
<View
@@ -31,29 +32,19 @@
app:layout_constraintBottom_toBottomOf="@id/volume_dialog_settings"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
- app:layout_constraintTop_toTopOf="@id/volume_ringer_drawer" />
-
- <View
- android:id="@+id/volume_ringer_horizontal_background"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:background="@drawable/volume_dialog_background"
- app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container"
- app:layout_constraintEnd_toEndOf="@id/volume_ringer_drawer"
- app:layout_constraintStart_toStartOf="@id/volume_ringer_drawer"
- app:layout_constraintTop_toTopOf="@id/volume_dialog_background" />
+ app:layout_constraintTop_toTopOf="@id/volume_dialog_main_slider_container" />
<include
android:id="@id/volume_ringer_drawer"
layout="@layout/volume_ringer_drawer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
- android:layout_marginRight="@dimen/volume_dialog_ringer_drawer_diff_right_margin"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginEnd="@dimen/volume_dialog_ringer_drawer_diff_end_margin"
+ android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="1" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"/>
<include
android:id="@+id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index cd8f18f7dcdb..983603a95833 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -18,13 +18,21 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/volume_ringer_drawer"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
android:layoutDirection="ltr"
app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
+ <View
+ android:id="@+id/ringer_buttons_background"
+ android:layout_width="@dimen/volume_dialog_width"
+ android:layout_height="0dp"
+ android:layout_marginTop="@dimen/volume_dialog_background_vertical_margin"
+ android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+ android:background="@drawable/volume_dialog_ringer_background" />
+
<!-- add ringer buttons here -->
</androidx.constraintlayout.motion.widget.MotionLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 570197edf876..33e29c11254c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2120,8 +2120,8 @@
<dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
- <dimen name="volume_dialog_ringer_drawer_left_margin">10dp</dimen>
- <dimen name="volume_dialog_ringer_drawer_diff_right_margin">6dp</dimen>
+ <dimen name="volume_dialog_ringer_drawer_margin">10dp</dimen>
+ <dimen name="volume_dialog_ringer_drawer_diff_end_margin">6dp</dimen>
<dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
<dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
<dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 42460617d476..a8de43332556 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -140,7 +140,7 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie
@Override
protected void onViewDetached() {
// TODO(b/117344873) Remove below work around after this issue be fixed.
- if (mDisplayId == mDisplayTracker.getDefaultDisplayId()) {
+ if (mDisplayId == mDisplayTracker.getDefaultDisplayId() && mLiveData != null) {
mLiveData.removeObserver(mObserver);
}
mConfigurationController.removeCallback(mConfigurationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
index 697d16b3336c..277c81ee9e9b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
@@ -48,8 +48,7 @@ import java.util.List;
* An adapter which shows the set of accessibility targets that can be performed.
*/
public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
- @VisibleForTesting
- static final int PAYLOAD_HEARING_STATUS_DRAWABLE = 1;
+ @VisibleForTesting static final int PAYLOAD_HEARING_STATUS_DRAWABLE = 1;
private int mIconWidthHeight;
private int mBadgeWidthHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt
new file mode 100644
index 000000000000..cb6d6f32923d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.systemui.keyboard.shortcut
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import com.android.systemui.CoreStartable
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
+import com.android.systemui.statusbar.CommandQueue
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+
+@SysUISingleton
+class ShortcutHelperCoreStartable
+@Inject constructor(
+ private val commandQueue: CommandQueue,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val stateRepository: ShortcutHelperStateRepository,
+ @Background private val backgroundScope: CoroutineScope,
+) : CoreStartable {
+ override fun start() {
+ registerBroadcastReceiver(
+ action = Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS,
+ onReceive = {
+ backgroundScope.launch { stateRepository.show() }
+ }
+ )
+ registerBroadcastReceiver(
+ action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
+ onReceive = { stateRepository.hide() }
+ )
+ registerBroadcastReceiver(
+ action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
+ onReceive = { stateRepository.hide() }
+ )
+ commandQueue.addCallback(
+ object : CommandQueue.Callbacks {
+ override fun dismissKeyboardShortcutsMenu() {
+ stateRepository.hide()
+ }
+
+ override fun toggleKeyboardShortcutsMenu(deviceId: Int) {
+ backgroundScope.launch { stateRepository.toggle(deviceId) }
+ }
+ }
+ )
+ }
+
+ private fun registerBroadcastReceiver(action: String, onReceive: () -> Unit) {
+ broadcastDispatcher.registerReceiver(
+ receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ onReceive()
+ }
+ },
+ filter = IntentFilter(action),
+ flags = Context.RECEIVER_EXPORTED or Context.RECEIVER_VISIBLE_TO_INSTANT_APPS,
+ user = UserHandle.ALL,
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
index 1af7340ad7b4..d8532c176619 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
@@ -21,7 +21,6 @@ import com.android.systemui.Flags.keyboardShortcutHelperRewrite
import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource
import com.android.systemui.keyboard.shortcut.data.source.CurrentAppShortcutsSource
import com.android.systemui.keyboard.shortcut.data.source.InputShortcutsSource
@@ -95,8 +94,8 @@ interface ShortcutHelperModule {
@Provides
@IntoMap
- @ClassKey(ShortcutHelperStateRepository::class)
- fun repo(implLazy: Lazy<ShortcutHelperStateRepository>): CoreStartable {
+ @ClassKey(ShortcutHelperCoreStartable::class)
+ fun repo(implLazy: Lazy<ShortcutHelperCoreStartable>): CoreStartable {
return if (keyboardShortcutHelperRewrite()) {
implLazy.get()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
index aa6b61b6215d..42a13f58cdf7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
@@ -16,73 +16,44 @@
package com.android.systemui.keyboard.shortcut.data.repository
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
import android.hardware.input.InputManager
-import android.os.UserHandle
import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
-import com.android.systemui.CoreStartable
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Inactive
import com.android.systemui.shared.hardware.findInputDevice
-import com.android.systemui.statusbar.CommandQueue
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
+import javax.inject.Inject
@SysUISingleton
class ShortcutHelperStateRepository
@Inject
constructor(
- private val commandQueue: CommandQueue,
- private val broadcastDispatcher: BroadcastDispatcher,
private val inputManager: InputManager,
- @Background private val backgroundScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
-) : CoreStartable {
+) {
+ private val _state = MutableStateFlow<ShortcutHelperState>(Inactive)
+ val state = _state.asStateFlow()
- val state = MutableStateFlow<ShortcutHelperState>(Inactive)
+ suspend fun toggle(deviceId: Int? = null) {
+ if (_state.value is Inactive) {
+ show(deviceId)
+ } else {
+ hide()
+ }
+ }
- override fun start() {
- registerBroadcastReceiver(
- action = Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS,
- onReceive = {
- backgroundScope.launch { state.value = Active(findPhysicalKeyboardId()) }
- }
- )
- registerBroadcastReceiver(
- action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
- onReceive = { state.value = Inactive }
- )
- registerBroadcastReceiver(
- action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
- onReceive = { state.value = Inactive }
- )
- commandQueue.addCallback(
- object : CommandQueue.Callbacks {
- override fun dismissKeyboardShortcutsMenu() {
- state.value = Inactive
- }
+ suspend fun show(deviceId: Int? = null) {
+ _state.value = Active(deviceId ?: findPhysicalKeyboardId())
+ }
- override fun toggleKeyboardShortcutsMenu(deviceId: Int) {
- state.value =
- if (state.value is Inactive) {
- Active(deviceId)
- } else {
- Inactive
- }
- }
- }
- )
+ fun hide() {
+ _state.value = Inactive
}
private suspend fun findPhysicalKeyboardId() =
@@ -92,21 +63,4 @@ constructor(
return@withContext firstEnabledPhysicalKeyboard?.id ?: VIRTUAL_KEYBOARD
}
- fun hide() {
- state.value = Inactive
- }
-
- private fun registerBroadcastReceiver(action: String, onReceive: () -> Unit) {
- broadcastDispatcher.registerReceiver(
- receiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- onReceive()
- }
- },
- filter = IntentFilter(action),
- flags = Context.RECEIVER_EXPORTED or Context.RECEIVER_VISIBLE_TO_INSTANT_APPS,
- user = UserHandle.ALL,
- )
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
index cea3b6442feb..5505189e3a33 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
@@ -23,10 +23,9 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.model.SysUiState
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.QuickStepContract
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.asStateFlow
+import javax.inject.Inject
import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
@@ -39,7 +38,7 @@ constructor(
private val repository: ShortcutHelperStateRepository
) {
- val state: Flow<ShortcutHelperState> = repository.state.asStateFlow()
+ val state: Flow<ShortcutHelperState> = repository.state
fun onViewClosed() {
repository.hide()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
index 82367ebb8c76..ee53471253af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
@@ -514,7 +514,7 @@ public class InternetDialogDelegateLegacy implements
}
private void setMobileDataLayout(InternetContent internetContent) {
- if (!internetContent.mShouldUpdateMobileNetwork && mDialog == null) {
+ if (!internetContent.mShouldUpdateMobileNetwork || mDialog == null) {
return;
}
setMobileDataLayout(mDialog, internetContent);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 8733eeb2bd05..58b8f6254d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -71,8 +71,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
fun CoroutineScope.bind(view: View) {
val volumeDialogBackgroundView = view.requireViewById<View>(R.id.volume_dialog_background)
- val ringerHBackgroundView =
- view.requireViewById<View>(R.id.volume_ringer_horizontal_background)
+ val ringerBackgroundView = view.requireViewById<View>(R.id.ringer_buttons_background)
val drawerContainer = view.requireViewById<MotionLayout>(R.id.volume_ringer_drawer)
val unselectedButtonUiModel = RingerButtonUiModel.getUnselectedButton(view.context)
val selectedButtonUiModel = RingerButtonUiModel.getSelectedButton(view.context)
@@ -86,15 +85,17 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
)
var backgroundAnimationProgress: Float by
Delegates.observable(0F) { _, _, progress ->
- ringerHBackgroundView.applyCorners(
+ ringerBackgroundView.applyCorners(
fullRadius = volumeDialogBgFullRadius,
diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius,
progress,
+ isBottom = false,
)
volumeDialogBackgroundView.applyCorners(
fullRadius = volumeDialogBgFullRadius,
diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius,
progress,
+ isBottom = true,
)
}
val ringerDrawerTransitionListener = VolumeDialogRingerDrawerTransitionListener {
@@ -102,7 +103,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()
- ringerHBackgroundView.background = ringerHBackgroundView.background.mutate()
+ ringerBackgroundView.background = ringerBackgroundView.background.mutate()
viewModel.ringerViewModel
.mapLatest { ringerState ->
@@ -192,8 +193,8 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
)
volumeDialogBackgroundView.background =
volumeDialogBackgroundView.background.mutate()
- ringerHBackgroundView.background =
- ringerHBackgroundView.background.mutate()
+ ringerBackgroundView.background =
+ ringerBackgroundView.background.mutate()
}
}
}
@@ -203,8 +204,8 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
- ringerHBackgroundView.setBackgroundResource(
- R.drawable.volume_dialog_background
+ ringerBackgroundView.setBackgroundResource(
+ R.drawable.volume_dialog_ringer_background
)
}
}
@@ -227,14 +228,14 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
) {
val count = uiModel.availableButtons.size
val selectedButton =
- getChildAt(count - uiModel.currentButtonIndex - 1)
+ getChildAt(count - uiModel.currentButtonIndex)
.requireViewById<ImageButton>(R.id.volume_drawer_button)
val previousIndex =
uiModel.availableButtons.indexOfFirst {
it?.ringerMode == uiModel.drawerState.previousMode
}
val unselectedButton =
- getChildAt(count - previousIndex - 1)
+ getChildAt(count - previousIndex)
.requireViewById<ImageButton>(R.id.volume_drawer_button)
// We only need to execute on roundness animation end and volume dialog background
// progress update once because these changes should be applied once on volume dialog
@@ -282,7 +283,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
val count = uiModel.availableButtons.size
uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
ringerButton?.let {
- val view = getChildAt(count - index - 1)
+ val view = getChildAt(count - index)
val isOpen = uiModel.drawerState is RingerDrawerState.Open
if (index == uiModel.currentButtonIndex) {
view.bindDrawerButton(
@@ -335,7 +336,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
private fun MotionLayout.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
- val childCountDelta = childCount - count
+ val childCountDelta = childCount - count - 1
when {
childCountDelta > 0 -> {
removeViews(0, childCountDelta)
@@ -395,8 +396,14 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
}
- private fun View.applyCorners(fullRadius: Int, diff: Int, progress: Float) {
- (background as GradientDrawable).cornerRadius = fullRadius - progress * diff
+ private fun View.applyCorners(fullRadius: Int, diff: Int, progress: Float, isBottom: Boolean) {
+ val radius = fullRadius - progress * diff
+ (background as GradientDrawable).cornerRadii =
+ if (isBottom) {
+ floatArrayOf(0F, 0F, 0F, 0F, radius, radius, radius, radius)
+ } else {
+ FloatArray(8) { radius }
+ }
background.invalidateSelf()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
index 69ffa3890437..c1e003727750 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.ringer.ui.util
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.util.TypedValue
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintSet
@@ -41,17 +42,6 @@ private fun ConstraintSet.setButtonPositionPortraitConstraints(
index: Int,
button: View,
) {
- if (motionLayout.getChildAt(index - 1) == null) {
- connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
- } else {
- connect(
- button.id,
- ConstraintSet.TOP,
- motionLayout.getChildAt(index - 1).id,
- ConstraintSet.BOTTOM,
- )
- }
-
if (motionLayout.getChildAt(index + 1) == null) {
connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
} else {
@@ -62,10 +52,7 @@ private fun ConstraintSet.setButtonPositionPortraitConstraints(
ConstraintSet.TOP,
)
}
- connect(button.id, ConstraintSet.START, motionLayout.id, ConstraintSet.START)
connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
- clear(button.id, ConstraintSet.LEFT)
- clear(button.id, ConstraintSet.RIGHT)
}
private fun ConstraintSet.setButtonPositionLandscapeConstraints(
@@ -73,93 +60,120 @@ private fun ConstraintSet.setButtonPositionLandscapeConstraints(
index: Int,
button: View,
) {
- if (motionLayout.getChildAt(index - 1) == null) {
- connect(button.id, ConstraintSet.LEFT, motionLayout.id, ConstraintSet.LEFT)
- } else {
- connect(
- button.id,
- ConstraintSet.LEFT,
- motionLayout.getChildAt(index - 1).id,
- ConstraintSet.RIGHT,
- )
- }
if (motionLayout.getChildAt(index + 1) == null) {
- connect(button.id, ConstraintSet.RIGHT, motionLayout.id, ConstraintSet.RIGHT)
+ connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
} else {
connect(
button.id,
- ConstraintSet.RIGHT,
+ ConstraintSet.END,
motionLayout.getChildAt(index + 1).id,
- ConstraintSet.LEFT,
+ ConstraintSet.START,
)
}
- connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
- clear(button.id, ConstraintSet.START)
- clear(button.id, ConstraintSet.END)
+
+ // Index 1 is the first button in the children of motionLayout.
+ if (index == 1) {
+ clear(button.id, ConstraintSet.START)
+ }
}
private fun ConstraintSet.adjustOpenConstraintsForDrawer(
motionLayout: MotionLayout,
lastOrientation: Int,
) {
- motionLayout.children.forEachIndexed { index, button ->
- setAlpha(button.id, 1.0F)
- constrainWidth(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- constrainHeight(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- when (lastOrientation) {
- ORIENTATION_LANDSCAPE -> {
- if (index == 0) {
- setMargin(
- button.id,
- ConstraintSet.LEFT,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_left_margin
- ),
- )
+ motionLayout.children.forEachIndexed { index, view ->
+ if (view.id != R.id.ringer_buttons_background) {
+ setAlpha(view.id, 1.0F)
+ constrainWidth(
+ view.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ constrainHeight(
+ view.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ when (lastOrientation) {
+ ORIENTATION_LANDSCAPE -> {
+ if (index == 1) {
+ setMargin(
+ view.id,
+ ConstraintSet.START,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_margin
+ ),
+ )
+ }
+ setButtonPositionLandscapeConstraints(motionLayout, index, view)
+ if (index != motionLayout.childCount - 1) {
+ setMargin(
+ view.id,
+ ConstraintSet.END,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_components_spacing
+ ),
+ )
+ } else {
+ setMargin(view.id, ConstraintSet.END, 0)
+ }
+ setMargin(view.id, ConstraintSet.BOTTOM, 0)
}
- setButtonPositionLandscapeConstraints(motionLayout, index, button)
- if (index != motionLayout.childCount - 1) {
- setMargin(
- button.id,
- ConstraintSet.RIGHT,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
- ),
- )
- } else {
- setMargin(button.id, ConstraintSet.RIGHT, 0)
+
+ ORIENTATION_PORTRAIT -> {
+ if (index == 1) {
+ setMargin(view.id, ConstraintSet.START, 0)
+ }
+ setButtonPositionPortraitConstraints(motionLayout, index, view)
+ if (index != motionLayout.childCount - 1) {
+ setMargin(
+ view.id,
+ ConstraintSet.BOTTOM,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_components_spacing
+ ),
+ )
+ } else {
+ setMargin(view.id, ConstraintSet.BOTTOM, 0)
+ }
+ setMargin(view.id, ConstraintSet.END, 0)
}
- setMargin(button.id, ConstraintSet.BOTTOM, 0)
}
- ORIENTATION_PORTRAIT -> {
- if (index == 0) {
- setMargin(button.id, ConstraintSet.LEFT, 0)
- }
- setButtonPositionPortraitConstraints(motionLayout, index, button)
- if (index != motionLayout.childCount - 1) {
- setMargin(
- button.id,
- ConstraintSet.BOTTOM,
+ } else {
+ constrainWidth(
+ view.id,
+ when (lastOrientation) {
+ ORIENTATION_LANDSCAPE ->
+ (motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ) * (motionLayout.childCount - 1)) +
+ (motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_margin
+ ) * 2) +
+ (motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_components_spacing
+ ) * (motionLayout.childCount - 2))
+
+ ORIENTATION_PORTRAIT ->
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
- ),
- )
- } else {
- setMargin(button.id, ConstraintSet.BOTTOM, 0)
- }
- setMargin(button.id, ConstraintSet.RIGHT, 0)
- }
+ R.dimen.volume_dialog_width
+ )
+
+ else -> 0
+ },
+ )
+ connect(view.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+ connect(
+ view.id,
+ ConstraintSet.START,
+ motionLayout.getChildAt(1).id,
+ ConstraintSet.START,
+ )
+ connect(view.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+ connect(view.id, ConstraintSet.TOP, motionLayout.getChildAt(1).id, ConstraintSet.TOP)
}
}
}
@@ -169,52 +183,91 @@ private fun ConstraintSet.adjustClosedConstraintsForDrawer(
selectedIndex: Int,
lastOrientation: Int,
) {
- motionLayout.children.forEachIndexed { index, button ->
- setMargin(button.id, ConstraintSet.RIGHT, 0)
- setMargin(button.id, ConstraintSet.BOTTOM, 0)
- when (lastOrientation) {
- ORIENTATION_LANDSCAPE -> {
- setButtonPositionLandscapeConstraints(motionLayout, index, button)
- if (selectedIndex != motionLayout.childCount - index - 1) {
- setAlpha(button.id, 0.0F)
- constrainWidth(button.id, 0)
- } else {
- setAlpha(button.id, 1.0F)
- constrainWidth(
- button.id,
+ motionLayout.children.forEachIndexed { index, view ->
+ if (view.id != R.id.ringer_buttons_background) {
+ setMargin(view.id, ConstraintSet.END, 0)
+ setMargin(view.id, ConstraintSet.BOTTOM, 0)
+ when (lastOrientation) {
+ ORIENTATION_LANDSCAPE -> {
+ setButtonPositionLandscapeConstraints(motionLayout, index, view)
+ if (selectedIndex != motionLayout.childCount - index - 1) {
+ setAlpha(view.id, 0.0F)
+ constrainWidth(
+ view.id,
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 1F,
+ motionLayout.context.resources.displayMetrics,
+ )
+ .toInt(),
+ )
+ } else {
+ connect(view.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+ setAlpha(view.id, 1.0F)
+ constrainWidth(
+ view.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ }
+ constrainHeight(
+ view.id,
motionLayout.context.resources.getDimensionPixelSize(
R.dimen.volume_dialog_ringer_drawer_button_size
),
)
}
- constrainHeight(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
- }
- ORIENTATION_PORTRAIT -> {
- setButtonPositionPortraitConstraints(motionLayout, index, button)
- if (selectedIndex != motionLayout.childCount - index - 1) {
- setAlpha(button.id, 0.0F)
- constrainHeight(button.id, 0)
- } else {
- setAlpha(button.id, 1.0F)
- constrainHeight(
- button.id,
+
+ ORIENTATION_PORTRAIT -> {
+ setButtonPositionPortraitConstraints(motionLayout, index, view)
+ if (selectedIndex != motionLayout.childCount - index - 1) {
+ setAlpha(view.id, 0.0F)
+ constrainHeight(
+ view.id,
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 1F,
+ motionLayout.context.resources.displayMetrics,
+ )
+ .toInt(),
+ )
+ } else {
+ setAlpha(view.id, 1.0F)
+ constrainHeight(
+ view.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ }
+ constrainWidth(
+ view.id,
motionLayout.context.resources.getDimensionPixelSize(
R.dimen.volume_dialog_ringer_drawer_button_size
),
)
}
- constrainWidth(
- button.id,
- motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_button_size
- ),
- )
}
+ } else {
+ constrainWidth(
+ view.id,
+ motionLayout.context.resources.getDimensionPixelSize(R.dimen.volume_dialog_width),
+ )
+ connect(view.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+ connect(
+ view.id,
+ ConstraintSet.START,
+ motionLayout.getChildAt(motionLayout.childCount - selectedIndex - 1).id,
+ ConstraintSet.START,
+ )
+ connect(view.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+ connect(
+ view.id,
+ ConstraintSet.TOP,
+ motionLayout.getChildAt(motionLayout.childCount - selectedIndex - 1).id,
+ ConstraintSet.TOP,
+ )
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
index 238551589361..8c2bdabb2417 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
@@ -832,6 +832,40 @@ public class InternetDialogDelegateLegacyTest extends SysuiTestCase {
});
}
+ @Test
+ public void updateDialog_shouldUpdateMobileNetworkTrue_updateMobileDataLayout() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true);
+ when(mInternetDetailsContentController.activeNetworkIsCellular()).thenReturn(false);
+ mMobileDataLayout.setVisibility(View.GONE);
+
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_shouldUpdateMobileNetworkFalse_doNotUpdateMobileDataLayout() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true);
+ when(mInternetDetailsContentController.activeNetworkIsCellular()).thenReturn(false);
+ mMobileDataLayout.setVisibility(View.GONE);
+
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
boolean connectedWifiVisible) {
mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 2641070a1a59..b8d26ef53fc7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -69,10 +69,7 @@ var Kosmos.shortcutHelperMultiTaskingShortcutsSource: KeyboardShortcutGroupsSour
val Kosmos.shortcutHelperStateRepository by
Kosmos.Fixture {
ShortcutHelperStateRepository(
- fakeCommandQueue,
- broadcastDispatcher,
fakeInputManager.inputManager,
- testScope,
testDispatcher,
)
}
@@ -153,10 +150,20 @@ val Kosmos.customShortcutCategoriesRepository by
)
}
+val Kosmos.shortcutHelperCoreStartable by
+ Kosmos.Fixture {
+ ShortcutHelperCoreStartable(
+ fakeCommandQueue,
+ broadcastDispatcher,
+ shortcutHelperStateRepository,
+ testScope,
+ )
+ }
+
val Kosmos.shortcutHelperTestHelper by
Kosmos.Fixture {
ShortcutHelperTestHelper(
- shortcutHelperStateRepository,
+ shortcutHelperCoreStartable,
applicationContext,
broadcastDispatcher,
fakeCommandQueue,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
index 8b45662e57e6..04eacaafcf91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
@@ -23,12 +23,13 @@ import android.view.KeyboardShortcutGroup
import android.view.WindowManager
import android.view.WindowManager.KeyboardShortcutsReceiver
import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.keyboard.shortcut.ShortcutHelperCoreStartable
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
class ShortcutHelperTestHelper(
- repo: ShortcutHelperStateRepository,
+ coreStartable: ShortcutHelperCoreStartable,
private val context: Context,
private val fakeBroadcastDispatcher: FakeBroadcastDispatcher,
private val fakeCommandQueue: FakeCommandQueue,
@@ -54,7 +55,7 @@ class ShortcutHelperTestHelper(
keyboardShortcutReceiver.onKeyboardShortcutsReceived(currentAppsShortcuts)
return@thenAnswer Unit
}
- repo.start()
+ coreStartable.start()
}
/**
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index ad87ceaf6f38..7f0bf0375b4a 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -79,6 +79,13 @@ flag {
}
flag {
+ name: "enable_autoclick_indicator"
+ namespace: "accessibility"
+ description: "Whether to show autoclick indicator when autoclick feature is enabled."
+ bug: "383901288"
+}
+
+flag {
name: "enable_a11y_checker_logging"
namespace: "accessibility"
description: "Whether to identify and log app a11y issues."
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index b095e3e2cff9..50c754008234 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -304,7 +304,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
cacheLastEvent(event, policyFlags, mLastMotionEvent == null || moved /* useAsAnchor */);
if (moved) {
- rescheduleClick(mDelay);
+ rescheduleClick(mDelay);
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
index c293087defb6..dc6afe17403d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
@@ -19,7 +19,9 @@ package com.android.server.appfunctions;
import android.annotation.NonNull;
import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
/** Service that manages app functions. */
@@ -28,7 +30,9 @@ public class AppFunctionManagerService extends SystemService {
public AppFunctionManagerService(Context context) {
super(context);
- mServiceImpl = new AppFunctionManagerServiceImpl(context);
+ mServiceImpl =
+ new AppFunctionManagerServiceImpl(
+ context, LocalServices.getService(PackageManagerInternal.class));
}
@Override
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 37276ddac75a..57d33f1a051e 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -50,6 +50,7 @@ import android.app.appsearch.observer.ObserverSpec;
import android.app.appsearch.observer.SchemaChangeInfo;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
@@ -87,8 +88,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
private final Context mContext;
private final Map<String, Object> mLocks = new WeakHashMap<>();
private final AppFunctionsLoggerWrapper mLoggerWrapper;
+ private final PackageManagerInternal mPackageManagerInternal;
- public AppFunctionManagerServiceImpl(@NonNull Context context) {
+ public AppFunctionManagerServiceImpl(
+ @NonNull Context context, @NonNull PackageManagerInternal packageManagerInternal) {
this(
context,
new RemoteServiceCallerImpl<>(
@@ -96,7 +99,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
new CallerValidatorImpl(context),
new ServiceHelperImpl(context),
new ServiceConfigImpl(),
- new AppFunctionsLoggerWrapper(context));
+ new AppFunctionsLoggerWrapper(context),
+ packageManagerInternal);
}
@VisibleForTesting
@@ -106,13 +110,15 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
CallerValidator callerValidator,
ServiceHelper appFunctionInternalServiceHelper,
ServiceConfig serviceConfig,
- AppFunctionsLoggerWrapper loggerWrapper) {
+ AppFunctionsLoggerWrapper loggerWrapper,
+ PackageManagerInternal packageManagerInternal) {
mContext = Objects.requireNonNull(context);
mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
mCallerValidator = Objects.requireNonNull(callerValidator);
mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
mServiceConfig = serviceConfig;
mLoggerWrapper = loggerWrapper;
+ mPackageManagerInternal = Objects.requireNonNull(packageManagerInternal);
}
/** Called when the user is unlocked. */
@@ -260,6 +266,24 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
"Cannot find the target service."));
return;
}
+ // Grant target app implicit visibility to the caller
+ final int grantRecipientUserId = targetUser.getIdentifier();
+ final int grantRecipientAppId =
+ UserHandle.getAppId(
+ mPackageManagerInternal.getPackageUid(
+ requestInternal
+ .getClientRequest()
+ .getTargetPackageName(),
+ /* flags= */ 0,
+ /* userId= */ grantRecipientUserId));
+ if (grantRecipientAppId > 0) {
+ mPackageManagerInternal.grantImplicitAccess(
+ grantRecipientUserId,
+ serviceIntent,
+ grantRecipientAppId,
+ callingUid,
+ /* direct= */ true);
+ }
bindAppFunctionServiceUnchecked(
requestInternal,
serviceIntent,
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
index 37a377950a87..071fda4f5d0c 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
@@ -31,8 +31,6 @@ import java.util.Objects;
class ServiceHelperImpl implements ServiceHelper {
private final Context mContext;
- // TODO(b/357551503): Keep track of unlocked users.
-
ServiceHelperImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 60516c39ffa7..41b4cbd9e074 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -757,9 +757,6 @@ public final class ActiveServices {
Message msg = obtainMessage(MSG_BG_START_TIMEOUT);
sendMessageAtTime(msg, when);
}
- if (mStartingBackground.size() < mMaxStartingBackground) {
- mAm.backgroundServicesFinishedLocked(mUserId);
- }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c9f06ac87269..b536dc524a80 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14374,10 +14374,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mBroadcastController.unbroadcastIntent(caller, intent, userId);
}
- void backgroundServicesFinishedLocked(int userId) {
- mBroadcastQueue.backgroundServicesFinishedLocked(userId);
- }
-
public void finishReceiver(IBinder caller, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
mBroadcastController.finishReceiver(caller, resultCode, resultData, resultExtras,
@@ -18936,7 +18932,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Settings.Global.BROADCAST_BG_CONSTANTS);
backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
- return new BroadcastQueueModernImpl(service, service.mHandler,
+ return new BroadcastQueueImpl(service, service.mHandler,
foreConstants, backConstants);
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index e676b1fca7fb..81d34f67dee9 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -111,7 +111,7 @@ public class BroadcastConstants {
public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum dispatch parallelism
+ * For {@link BroadcastQueueImpl}: Maximum dispatch parallelism
* that we'll tolerate for ordinary broadcast dispatch.
*/
public int MAX_RUNNING_PROCESS_QUEUES = DEFAULT_MAX_RUNNING_PROCESS_QUEUES;
@@ -120,7 +120,7 @@ public class BroadcastConstants {
ActivityManager.isLowRamDeviceStatic() ? 2 : 4;
/**
- * For {@link BroadcastQueueModernImpl}: Additional running process queue parallelism beyond
+ * For {@link BroadcastQueueImpl}: Additional running process queue parallelism beyond
* {@link #MAX_RUNNING_PROCESS_QUEUES} for dispatch of "urgent" broadcasts.
*/
public int EXTRA_RUNNING_URGENT_PROCESS_QUEUES = DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES;
@@ -129,7 +129,7 @@ public class BroadcastConstants {
private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of consecutive urgent
+ * For {@link BroadcastQueueImpl}: Maximum number of consecutive urgent
* broadcast dispatches allowed before letting broadcasts in lower priority queue
* to be scheduled in order to avoid starvation.
*/
@@ -139,7 +139,7 @@ public class BroadcastConstants {
private static final int DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES = 3;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of consecutive normal
+ * For {@link BroadcastQueueImpl}: Maximum number of consecutive normal
* broadcast dispatches allowed before letting broadcasts in lower priority queue
* to be scheduled in order to avoid starvation.
*/
@@ -149,7 +149,7 @@ public class BroadcastConstants {
private static final int DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES = 10;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
+ * For {@link BroadcastQueueImpl}: Maximum number of active broadcasts
* to dispatch to a "running" process queue before we retire them back to
* being "runnable" to give other processes a chance to run.
*/
@@ -160,7 +160,7 @@ public class BroadcastConstants {
ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of active "blocking" broadcasts
+ * For {@link BroadcastQueueImpl}: Maximum number of active "blocking" broadcasts
* to dispatch to a "running" System process queue before we retire them back to
* being "runnable" to give other processes a chance to run. Here "blocking" refers to
* whether or not we are going to block on the finishReceiver() to be called before moving
@@ -173,7 +173,7 @@ public class BroadcastConstants {
ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of active non-"blocking" broadcasts
+ * For {@link BroadcastQueueImpl}: Maximum number of active non-"blocking" broadcasts
* to dispatch to a "running" System process queue before we retire them back to
* being "runnable" to give other processes a chance to run. Here "blocking" refers to
* whether or not we are going to block on the finishReceiver() to be called before moving
@@ -187,7 +187,7 @@ public class BroadcastConstants {
ActivityManager.isLowRamDeviceStatic() ? 32 : 64;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of pending
+ * For {@link BroadcastQueueImpl}: Maximum number of pending
* broadcasts to hold for a process before we ignore any delays that policy
* might have applied to that process.
*/
@@ -197,7 +197,7 @@ public class BroadcastConstants {
ActivityManager.isLowRamDeviceStatic() ? 128 : 256;
/**
- * For {@link BroadcastQueueModernImpl}: Delay to apply to normal
+ * For {@link BroadcastQueueImpl}: Delay to apply to normal
* broadcasts, giving a chance for debouncing of rapidly changing events.
*/
public long DELAY_NORMAL_MILLIS = DEFAULT_DELAY_NORMAL_MILLIS;
@@ -205,7 +205,7 @@ public class BroadcastConstants {
private static final long DEFAULT_DELAY_NORMAL_MILLIS = +500;
/**
- * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
+ * For {@link BroadcastQueueImpl}: Delay to apply to broadcasts
* targeting cached applications.
*/
public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
@@ -213,7 +213,7 @@ public class BroadcastConstants {
private static final long DEFAULT_DELAY_CACHED_MILLIS = +120_000;
/**
- * For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
+ * For {@link BroadcastQueueImpl}: Delay to apply to urgent
* broadcasts, typically a negative value to indicate they should be
* executed before most other pending broadcasts.
*/
@@ -222,7 +222,7 @@ public class BroadcastConstants {
private static final long DEFAULT_DELAY_URGENT_MILLIS = -120_000;
/**
- * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts to
+ * For {@link BroadcastQueueImpl}: Delay to apply to broadcasts to
* foreground processes, typically a negative value to indicate they should be
* executed before most other pending broadcasts.
*/
@@ -232,7 +232,7 @@ public class BroadcastConstants {
private static final long DEFAULT_DELAY_FOREGROUND_PROC_MILLIS = -120_000;
/**
- * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts to
+ * For {@link BroadcastQueueImpl}: Delay to apply to broadcasts to
* persistent processes, typically a negative value to indicate they should be
* executed before most other pending broadcasts.
*/
@@ -242,7 +242,7 @@ public class BroadcastConstants {
private static final long DEFAULT_DELAY_PERSISTENT_PROC_MILLIS = -120_000;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of complete
+ * For {@link BroadcastQueueImpl}: Maximum number of complete
* historical broadcasts to retain for debugging purposes.
*/
public int MAX_HISTORY_COMPLETE_SIZE = DEFAULT_MAX_HISTORY_COMPLETE_SIZE;
@@ -251,7 +251,7 @@ public class BroadcastConstants {
ActivityManager.isLowRamDeviceStatic() ? 64 : 256;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of summarized
+ * For {@link BroadcastQueueImpl}: Maximum number of summarized
* historical broadcasts to retain for debugging purposes.
*/
public int MAX_HISTORY_SUMMARY_SIZE = DEFAULT_MAX_HISTORY_SUMMARY_SIZE;
@@ -268,7 +268,7 @@ public class BroadcastConstants {
private static final boolean DEFAULT_CORE_DEFER_UNTIL_ACTIVE = true;
/**
- * For {@link BroadcastQueueModernImpl}: How frequently we should check for the pending
+ * For {@link BroadcastQueueImpl}: How frequently we should check for the pending
* cold start validity.
*/
public long PENDING_COLD_START_CHECK_INTERVAL_MILLIS =
@@ -278,7 +278,7 @@ public class BroadcastConstants {
private static final long DEFAULT_PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 30_000;
/**
- * For {@link BroadcastQueueModernImpl}: Maximum number of outgoing broadcasts from a
+ * For {@link BroadcastQueueImpl}: Maximum number of outgoing broadcasts from a
* freezable process that will be allowed before killing the process.
*/
public int MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS;
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
index 6ddf60b369e6..700cf9c8deb8 100644
--- a/services/core/java/com/android/server/am/BroadcastHistory.java
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -164,7 +164,7 @@ public class BroadcastHistory {
@NeverCompile
public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage,
- @Nullable String dumpIntentAction, @NonNull String queueName,
+ @Nullable String dumpIntentAction,
@NonNull SimpleDateFormat sdf, boolean dumpAll) {
boolean needSep = true;
dumpBroadcastList(pw, sdf, mFrozenBroadcasts, dumpIntentAction, dumpAll, "Frozen");
@@ -198,18 +198,18 @@ public class BroadcastHistory {
pw.println();
}
needSep = true;
- pw.println(" Historical broadcasts [" + queueName + "]:");
+ pw.println(" Historical broadcasts:");
printed = true;
}
if (dumpIntentAction != null) {
- pw.print(" Historical Broadcast " + queueName + " #");
+ pw.print(" Historical Broadcast #");
pw.print(i); pw.println(":");
r.dump(pw, " ", sdf);
if (!dumpAll) {
break;
}
} else if (dumpAll) {
- pw.print(" Historical Broadcast " + queueName + " #");
+ pw.print(" Historical Broadcast #");
pw.print(i); pw.println(":");
r.dump(pw, " ", sdf);
} else {
@@ -256,7 +256,7 @@ public class BroadcastHistory {
pw.println();
}
needSep = true;
- pw.println(" Historical broadcasts summary [" + queueName + "]:");
+ pw.println(" Historical broadcasts summary:");
printed = true;
}
if (!dumpAll && i >= 50) {
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index ed3cd1ea03c8..db0562f5750a 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -453,7 +453,7 @@ class BroadcastProcessQueue {
*
* @return if this operation may have changed internal state, indicating
* that the caller is responsible for invoking
- * {@link BroadcastQueueModernImpl#updateRunnableList}
+ * {@link BroadcastQueueImpl#updateRunnableList}
*/
@CheckResult
public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate,
@@ -502,7 +502,7 @@ class BroadcastProcessQueue {
*
* @return if this operation may have changed internal state, indicating
* that the caller is responsible for invoking
- * {@link BroadcastQueueModernImpl#updateRunnableList}
+ * {@link BroadcastQueueImpl#updateRunnableList}
*/
@CheckResult
public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground,
@@ -837,7 +837,7 @@ class BroadcastProcessQueue {
/**
* @return if this operation may have changed internal state, indicating
* that the caller is responsible for invoking
- * {@link BroadcastQueueModernImpl#updateRunnableList}
+ * {@link BroadcastQueueImpl#updateRunnableList}
*/
@CheckResult
boolean forceDelayBroadcastDelivery(long delayedDurationMs) {
@@ -921,7 +921,7 @@ class BroadcastProcessQueue {
*
* @return if this operation may have changed internal state, indicating
* that the caller is responsible for invoking
- * {@link BroadcastQueueModernImpl#updateRunnableList}
+ * {@link BroadcastQueueImpl#updateRunnableList}
*/
@CheckResult
@VisibleForTesting
@@ -945,7 +945,7 @@ class BroadcastProcessQueue {
*
* @return if this operation may have changed internal state, indicating
* that the caller is responsible for invoking
- * {@link BroadcastQueueModernImpl#updateRunnableList}
+ * {@link BroadcastQueueImpl#updateRunnableList}
*/
@CheckResult
boolean removePrioritizeEarliestRequest() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a7d74a9fe54a..5b5ceaae718f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -49,14 +49,12 @@ public abstract class BroadcastQueue {
final @NonNull Handler mHandler;
final @NonNull BroadcastSkipPolicy mSkipPolicy;
final @NonNull BroadcastHistory mHistory;
- final @NonNull String mQueueName;
BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler,
- @NonNull String name, @NonNull BroadcastSkipPolicy skipPolicy,
+ @NonNull BroadcastSkipPolicy skipPolicy,
@NonNull BroadcastHistory history) {
mService = Objects.requireNonNull(service);
mHandler = Objects.requireNonNull(handler);
- mQueueName = Objects.requireNonNull(name);
mSkipPolicy = Objects.requireNonNull(skipPolicy);
mHistory = Objects.requireNonNull(history);
}
@@ -87,11 +85,6 @@ public abstract class BroadcastQueue {
TAG, cookie);
}
- @Override
- public String toString() {
- return mQueueName;
- }
-
public abstract void start(@NonNull ContentResolver resolver);
/**
@@ -129,9 +122,6 @@ public abstract class BroadcastQueue {
@Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
boolean waitForServices);
- @GuardedBy("mService")
- public abstract void backgroundServicesFinishedLocked(int userId);
-
/**
* Signal from OS internals that the given process has just been actively
* attached, and is ready to begin receiving broadcasts.
@@ -244,8 +234,6 @@ public abstract class BroadcastQueue {
/**
* Delays delivering broadcasts to the specified package.
- *
- * <p> Note that this is only valid for modern queue.
*/
public void forceDelayBroadcastDelivery(@NonNull String targetPackage,
long delayedDurationMs) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.md b/services/core/java/com/android/server/am/BroadcastQueue.md
index 81317932ef9b..16c40285dd93 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.md
+++ b/services/core/java/com/android/server/am/BroadcastQueue.md
@@ -26,7 +26,7 @@ process that isn't currently running.
## Per-process queues
-The design of `BroadcastQueueModernImpl` is centered around maintaining a
+The design of `BroadcastQueueImpl` is centered around maintaining a
separate `BroadcastProcessQueue` instance for each potential process on the
device. At this level, a process refers to the `android:process` attributes
defined in `AndroidManifest.xml` files, which means it can be defined and
@@ -57,7 +57,7 @@ except for those explicitly provided by "ordered" or "prioritized" broadcasts.
## Parallel dispatch
Given a collection of per-process queues with valid _runnable at_ timestamps,
-BroadcastQueueModernImpl is then willing to promote those _runnable_ queues
+BroadcastQueueImpl is then willing to promote those _runnable_ queues
into a _running_ state. We choose the next per-process queue to promote based
on the sorted ordering of the _runnable at_ timestamps, selecting the
longest-waiting process first, which aims to reduce overall broadcast dispatch
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index b270513e5563..36035bdcddbc 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -137,17 +137,17 @@ import java.util.function.Predicate;
* {@link #finishReceiverLocked}
* </ol>
*/
-class BroadcastQueueModernImpl extends BroadcastQueue {
- BroadcastQueueModernImpl(ActivityManagerService service, Handler handler,
+class BroadcastQueueImpl extends BroadcastQueue {
+ BroadcastQueueImpl(ActivityManagerService service, Handler handler,
BroadcastConstants fgConstants, BroadcastConstants bgConstants) {
this(service, handler, fgConstants, bgConstants, new BroadcastSkipPolicy(service),
new BroadcastHistory(fgConstants));
}
- BroadcastQueueModernImpl(ActivityManagerService service, Handler handler,
+ BroadcastQueueImpl(ActivityManagerService service, Handler handler,
BroadcastConstants fgConstants, BroadcastConstants bgConstants,
BroadcastSkipPolicy skipPolicy, BroadcastHistory history) {
- super(service, handler, "modern", skipPolicy, history);
+ super(service, handler, skipPolicy, history);
// For the moment, read agnostic constants from foreground
mConstants = Objects.requireNonNull(fgConstants);
@@ -545,8 +545,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
- if (DEBUG_BROADCAST) logv("Promoting " + queue
- + " from runnable to running; process is " + queue.app);
+ if (DEBUG_BROADCAST) {
+ logv("Promoting " + queue + " from runnable to running; process is " + queue.app);
+ }
promoteToRunningLocked(queue);
boolean completed;
if (processWarm) {
@@ -1198,12 +1199,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (receiver instanceof BroadcastFilter) {
notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
thread.scheduleRegisteredReceiver(
- ((BroadcastFilter) receiver).receiverList.receiver,
- receiverIntent, r.resultCode, r.resultData, r.resultExtras,
- r.ordered, r.initialSticky, assumeDelivered, r.userId,
- app.mState.getReportedProcState(),
- r.shareIdentity ? r.callingUid : Process.INVALID_UID,
- r.shareIdentity ? r.callerPackage : null);
+ ((BroadcastFilter) receiver).receiverList.receiver,
+ receiverIntent, r.resultCode, r.resultData, r.resultExtras,
+ r.ordered, r.initialSticky, assumeDelivered, r.userId,
+ app.mState.getReportedProcState(),
+ r.shareIdentity ? r.callingUid : Process.INVALID_UID,
+ r.shareIdentity ? r.callerPackage : null);
// TODO: consider making registered receivers of unordered
// broadcasts report results to detect ANRs
if (assumeDelivered) {
@@ -1922,13 +1923,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
return getRunningSize() + " running";
}
- @GuardedBy("mService")
- @Override
- public void backgroundServicesFinishedLocked(int userId) {
- // Modern queue does not alter the broadcasts delivery behavior based on background
- // services, so ignore.
- }
-
private void checkHealth() {
synchronized (mService) {
checkHealthLocked();
@@ -2418,7 +2412,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
@GuardedBy("mService")
public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
- proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
mHistory.dumpDebug(proto);
proto.end(token);
}
@@ -2448,7 +2441,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (dumpHistory) {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- needSep = mHistory.dumpLocked(ipw, dumpPackage, dumpIntentAction, mQueueName,
+ needSep = mHistory.dumpLocked(ipw, dumpPackage, dumpIntentAction,
sdf, dumpAll);
}
return needSep;
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 1a6051b6ac9c..cc6fabc8fd67 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -21,6 +21,7 @@ per-file FgsTempAllowList.java = file:/ACTIVITY_MANAGER_OWNERS
per-file HostingRecord.java = file:/ACTIVITY_MANAGER_OWNERS
per-file App*ExitInfo* = file:/ACTIVITY_MANAGER_OWNERS
per-file appexitinfo.proto = file:/ACTIVITY_MANAGER_OWNERS
+per-file UidObserverController* = file:/ACTIVITY_MANAGER_OWNERS
per-file App*StartInfo* = file:/PERFORMANCE_OWNERS
per-file appstartinfo.proto = file:/PERFORMANCE_OWNERS
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bddde9d589f3..2216f2769826 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3212,7 +3212,6 @@ public final class ProcessList {
if ((pid > 0 && pid != ActivityManagerService.MY_PID)
|| (pid == 0 && app.isPendingStart())) {
if (pid > 0) {
- mService.removePidLocked(pid, app);
app.setBindMountPending(false);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
@@ -3230,6 +3229,12 @@ public final class ProcessList {
}
}
app.killLocked(reason, reasonCode, subReason, true, async);
+ if (pid > 0) {
+ // Remove pid record mapping after killing the process, so there won't be a short
+ // period that the app is still alive but its access to system may be illegal due
+ // to no existing record for its pid.
+ mService.removePidLocked(pid, app);
+ }
mService.handleAppDiedLocked(app, pid, willRestart, allowRestart,
false /* fromBinderDied */);
if (willRestart) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c314ab06fbad..3f915757f137 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -369,16 +369,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void onBootPhase(int phase) {
super.onBootPhase(phase);
- if (phase == PHASE_ACTIVITY_MANAGER_READY) {
- mLockSettingsService.migrateOldDataAfterSystemReady();
- mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
- } else if (phase == PHASE_BOOT_COMPLETED) {
- // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
- // build can no longer occur. This is the time to destroy any migrated protectors.
- mLockSettingsService.destroyMigratedProtectors();
-
- mLockSettingsService.loadEscrowData();
- }
+ mLockSettingsService.onBootPhase(phase);
}
@Override
@@ -397,6 +388,21 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ private void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ migrateOldDataAfterSystemReady();
+ deleteRepairModePersistentDataIfNeeded();
+ } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ mHandler.post(() -> {
+ // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
+ // build can no longer occur. This is the time to destroy any migrated protectors.
+ destroyMigratedProtectors();
+
+ loadEscrowData();
+ });
+ }
+ }
+
@VisibleForTesting
protected static class SynchronizedStrongAuthTracker
extends LockPatternUtils.StrongAuthTracker {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsStore.java b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
index d83d355fce31..b688d4b3ecde 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsStore.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
@@ -36,6 +36,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
@@ -242,8 +243,10 @@ public class PowerStatsStore {
// Lock the directory from access by other JVMs
try {
- mLockFile.getParentFile().mkdirs();
- mLockFile.createNewFile();
+ if (!mLockFile.exists()) {
+ mLockFile.getParentFile().mkdirs();
+ mLockFile.createNewFile();
+ }
mJvmLock = FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
} catch (IOException e) {
Slog.e(TAG, "Cannot lock snapshot directory", e);
@@ -252,10 +255,13 @@ public class PowerStatsStore {
private void unlockStoreDirectory() {
try {
- mJvmLock.close();
+ Channel channel = mJvmLock.acquiredBy();
+ mJvmLock.release();
+ channel.close();
} catch (IOException e) {
Slog.e(TAG, "Cannot unlock snapshot directory", e);
} finally {
+ mJvmLock = null;
mFileLock.unlock();
}
}
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index 5e048810cc97..c8dbbd29823c 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -87,3 +87,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "extended_battery_history_continuous_collection_enabled"
+ namespace: "backstage_power"
+ description: "Disable automatic reset of battery stats history on full charge"
+ bug: "381940953"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index dbe0faf942d9..94830fa11134 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -97,6 +97,14 @@ class BLASTSyncEngine {
void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
default void onTransactionCommitTimeout() {}
default void onReadyTimeout() {}
+
+ default void onReadyTraceStart(String name, int id) {
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, name, id);
+ }
+
+ default void onReadyTraceEnd(String name, int id) {
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, name, id);
+ }
}
/**
@@ -149,8 +157,8 @@ class BLASTSyncEngine {
}
};
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
- mTraceName = name + "SyncGroupReady";
- Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, mTraceName, id);
+ mTraceName = name + "-SyncReady#" + id;
+ listener.onReadyTraceStart(mTraceName, id);
}
}
@@ -209,7 +217,7 @@ class BLASTSyncEngine {
private void finishNow() {
if (mTraceName != null) {
- Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, mTraceName, mSyncId);
+ mListener.onReadyTraceEnd(mTraceName, mSyncId);
}
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
@@ -225,9 +233,7 @@ class BLASTSyncEngine {
wc.waitForSyncTransactionCommit(wcAwaitingCommit);
}
- final int syncId = mSyncId;
final long mergedTxId = merged.getId();
- final String syncName = mSyncName;
class CommitCallback implements Runnable {
// Can run a second time if the action completes after the timeout.
boolean ran = false;
@@ -254,7 +260,7 @@ class BLASTSyncEngine {
// a trace. Since these kind of ANRs can trigger such an issue,
// try and ensure we will have some visibility in both cases.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout");
- Slog.e(TAG, "WM sent Transaction (#" + syncId + ", " + syncName + ", tx="
+ Slog.e(TAG, "WM sent Transaction (#" + mSyncId + ", " + mSyncName + ", tx="
+ mergedTxId + ") to organizer, but never received commit callback."
+ " Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4c2d8492197c..b9febb83b780 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -578,7 +578,10 @@ public class BackgroundActivityStartController {
sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
sb.append("; balAllowedByPiCreatorWithHardening: ")
.append(mBalAllowedByPiCreatorWithHardening);
- sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
+ if (mResultForCaller != null) {
+ sb.append("; resultIfPiCreatorAllowsBal: ")
+ .append(balCodeToString(mResultForCaller.mCode));
+ }
sb.append("; callerStartMode: ").append(balStartModeToString(
mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
sb.append("; hasRealCaller: ").append(hasRealCaller());
@@ -607,7 +610,10 @@ public class BackgroundActivityStartController {
.append(mRealCallerApp.hasActivityInVisibleTask());
}
sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
- sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
+ if (mResultForRealCaller != null) {
+ sb.append("; resultIfPiSenderAllowsBal: ")
+ .append(balCodeToString(mResultForRealCaller.mCode));
+ }
sb.append("; realCallerStartMode: ").append(balStartModeToString(
mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a3d71dbc5ed1..e7ed61ae57fb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -137,9 +137,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
private static final String TAG = "Transition";
private static final String TRACE_NAME_PLAY_TRANSITION = "playing";
- /** The default package for resources */
- private static final String DEFAULT_PACKAGE = "android";
-
/** The transition has been created but isn't collecting yet. */
private static final int STATE_PENDING = -1;
@@ -3405,6 +3402,16 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_WINDOW_MANAGER, TAG, cookie);
}
+ @Override
+ public void onReadyTraceStart(String name, int id) {
+ asyncTraceBegin(name, id);
+ }
+
+ @Override
+ public void onReadyTraceEnd(String name, int id) {
+ asyncTraceEnd(id);
+ }
+
boolean hasChanged(WindowContainer wc) {
final ChangeInfo chg = mChanges.get(wc);
if (chg == null) return false;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index f634beb77329..65cf4ee733dd 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -44,6 +44,7 @@
#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
#include <binder/IServiceManager.h>
#include <com_android_input_flags.h>
+#include <dispatcher/Entry.h>
#include <include/gestures.h>
#include <input/Input.h>
#include <input/PointerController.h>
@@ -66,6 +67,7 @@
#include <atomic>
#include <cinttypes>
#include <map>
+#include <variant>
#include <vector>
#include "android_hardware_display_DisplayTopology.h"
@@ -416,8 +418,9 @@ public:
void interceptMotionBeforeQueueing(ui::LogicalDisplayId displayId, uint32_t source,
int32_t action, nsecs_t when,
uint32_t& policyFlags) override;
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
- uint32_t policyFlags) override;
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
+ uint32_t policyFlags) override;
std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent& keyEvent,
uint32_t policyFlags) override;
void pokeUserActivity(nsecs_t eventTime, int32_t eventType,
@@ -1905,9 +1908,9 @@ bool NativeInputManager::isDisplayInteractive(ui::LogicalDisplayId displayId) {
return true;
}
-nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent& keyEvent,
- uint32_t policyFlags) {
+std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
+ const KeyEvent& keyEvent, uint32_t policyFlags) {
ATRACE_CALL();
// Policy:
// - Ignore untrusted events and pass them along.
@@ -1935,7 +1938,19 @@ nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& tok
if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching")) {
return 0;
}
- return delayMillis < 0 ? -1 : milliseconds_to_nanoseconds(delayMillis);
+
+ // Negative delay represent states from intercepting the key.
+ // 0 : Continue event.
+ if (delayMillis == 0) {
+ return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE;
+ }
+
+ // -1 : Drop and skip the key event.
+ if (delayMillis == -1) {
+ return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
+ }
+
+ return milliseconds_to_nanoseconds(delayMillis);
}
std::optional<KeyEvent> NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token,
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 018cf20e914f..c62cd6e962b3 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -469,8 +469,9 @@ class PermissionService(private val service: AccessCheckingService) :
permissionName: String,
deviceId: String
): Int {
+ val pid = Binder.getCallingPid()
val uid = Binder.getCallingUid()
- val result = context.checkPermission(permissionName, Binder.getCallingPid(), uid)
+ val result = context.checkPermission(permissionName, pid, uid)
if (result == PackageManager.PERMISSION_GRANTED) {
return Context.PERMISSION_REQUEST_STATE_GRANTED
}
@@ -478,17 +479,15 @@ class PermissionService(private val service: AccessCheckingService) :
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val packageState =
- packageManagerLocal.withFilteredSnapshot(uid, userId).use {
- it.getPackageState(packageName)
- } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
- val androidPackage = packageState.androidPackage
- ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ packageManagerLocal.withFilteredSnapshot(uid, userId).use {
+ it.getPackageState(packageName)
+ } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ val androidPackage =
+ packageState.androidPackage ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
if (appId != packageState.appId) {
return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
}
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- }
+ val permission = service.getState { with(policy) { getPermissions()[permissionName] } }
if (permission == null || !permission.isRuntime) {
return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
}
@@ -496,10 +495,37 @@ class PermissionService(private val service: AccessCheckingService) :
return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
}
- val permissionFlags = service.getState {
- getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val permissionFlags =
+ service.getState {
+ getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ }
+ val isUnreqestable = permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)
+ // Special case for READ_MEDIA_IMAGES due to photo picker
+ if ((permissionName == Manifest.permission.READ_MEDIA_IMAGES ||
+ permissionName == Manifest.permission.READ_MEDIA_VIDEO) && isUnreqestable) {
+ val isUserSelectedGranted =
+ context.checkPermission(
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
+ pid,
+ uid,
+ ) == PackageManager.PERMISSION_GRANTED
+ val userSelectedPermissionFlags =
+ service.getState {
+ getPermissionFlagsWithPolicy(
+ appId,
+ userId,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
+ deviceId,
+ )
+ }
+ if (
+ isUserSelectedGranted &&
+ userSelectedPermissionFlags.hasBits(PermissionFlags.USER_FIXED)
+ ) {
+ return Context.PERMISSION_REQUEST_STATE_REQUESTABLE
+ }
}
- return if (permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)) {
+ return if (isUnreqestable) {
Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
} else {
Context.PERMISSION_REQUEST_STATE_REQUESTABLE
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
index a69e9025bfa0..9aaf9ceda93f 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
@@ -18,28 +18,35 @@ package com.android.server.appfunctions
import android.app.appfunctions.flags.Flags
import android.content.Context
+import android.content.pm.PackageManagerInternal
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.LocalServices
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
class AppFunctionManagerServiceImplTest {
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@get:Rule
- val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java).build()
private val context: Context
get() = ApplicationProvider.getApplicationContext()
- private val serviceImpl = AppFunctionManagerServiceImpl(context)
+ private val serviceImpl = AppFunctionManagerServiceImpl(context, mock<PackageManagerInternal>())
@Test
fun testGetLockForPackage_samePackage() {
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt
index 896d2a21d0ac..687acf569d15 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt
@@ -25,11 +25,13 @@ import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback
import android.app.appsearch.GenericDocument
import android.content.Context
import android.content.pm.PackageManager
+import android.content.pm.PackageManagerInternal
import android.os.UserHandle
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.LocalServices
import com.google.common.util.concurrent.MoreExecutors
import org.junit.Before
import org.junit.Rule
@@ -40,24 +42,25 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-
-/**
- * Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values.
- */
+/** Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values. */
@RunWith(AndroidJUnit4::class)
class AppFunctionsLoggingTest {
@get:Rule
val mExtendedMockitoRule: ExtendedMockitoRule =
ExtendedMockitoRule.Builder(this)
.mockStatic(AppFunctionsStatsLog::class.java)
+ .mockStatic(LocalServices::class.java)
.build()
- private val mContext: Context get() = ApplicationProvider.getApplicationContext()
+ private val mContext: Context
+ get() = ApplicationProvider.getApplicationContext()
+
private val mMockPackageManager = mock<PackageManager>()
private val mAppFunctionsLoggerWrapper =
AppFunctionsLoggerWrapper(
mMockPackageManager,
MoreExecutors.directExecutor(),
- { TEST_CURRENT_TIME_MILLIS })
+ { TEST_CURRENT_TIME_MILLIS },
+ )
private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback
private val mServiceImpl =
@@ -67,25 +70,40 @@ class AppFunctionsLoggingTest {
mock<CallerValidator>(),
mock<ServiceHelper>(),
ServiceConfigImpl(),
- mAppFunctionsLoggerWrapper)
+ mAppFunctionsLoggerWrapper,
+ mock<PackageManagerInternal>(),
+ )
- private val mRequestInternal = ExecuteAppFunctionAidlRequest(
- ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
- UserHandle.CURRENT, TEST_CALLING_PKG, TEST_INITIAL_REQUEST_TIME_MILLIS
- )
+ private val mRequestInternal =
+ ExecuteAppFunctionAidlRequest(
+ ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
+ UserHandle.CURRENT,
+ TEST_CALLING_PKG,
+ TEST_INITIAL_REQUEST_TIME_MILLIS,
+ )
@Before
fun setup() {
- whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>())).thenReturn(TEST_TARGET_UID)
- mSafeCallback = mServiceImpl.initializeSafeExecuteAppFunctionCallback(mRequestInternal, mock<IExecuteAppFunctionCallback>(), TEST_CALLING_UID)
+ whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>()))
+ .thenReturn(TEST_TARGET_UID)
+ mSafeCallback =
+ mServiceImpl.initializeSafeExecuteAppFunctionCallback(
+ mRequestInternal,
+ mock<IExecuteAppFunctionCallback>(),
+ TEST_CALLING_UID,
+ )
mSafeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
}
@Test
fun testOnSuccess_logsSuccessResponse() {
val response =
- ExecuteAppFunctionResponse(GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
- .setPropertyLong("longProperty", 42L).setPropertyString("stringProperty", "text").build())
+ ExecuteAppFunctionResponse(
+ GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+ .setPropertyLong("longProperty", 42L)
+ .setPropertyString("stringProperty", "text")
+ .build()
+ )
mSafeCallback.onResult(response)
@@ -98,14 +116,16 @@ class AppFunctionsLoggingTest {
/* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
/* responseSizeBytes= */ eq<Int>(response.responseDataSize),
/* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
- /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS)
+ /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
)
}
}
@Test
fun testOnError_logsFailureResponse() {
- mSafeCallback.onError(AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied"))
+ mSafeCallback.onError(
+ AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied")
+ )
ExtendedMockito.verify {
AppFunctionsStatsLog.write(
@@ -116,7 +136,7 @@ class AppFunctionsLoggingTest {
/* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
/* responseSizeBytes= */ eq<Int>(0),
/* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
- /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS)
+ /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
)
}
}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 0d25426700a6..f37ca7cc3fad 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -238,7 +238,7 @@ test_module_config {
include_filters: [
"com.android.server.am.BroadcastQueueTest",
"com.android.server.am.BroadcastRecordTest",
- "com.android.server.am.BroadcastQueueModernImplTest",
+ "com.android.server.am.BroadcastQueueImplTest",
],
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueImplTest.java
index 82237bca2e34..1e665c2c5c50 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueImplTest.java
@@ -96,8 +96,8 @@ import java.util.List;
import java.util.Objects;
@SmallTest
-public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
- private static final String TAG = "BroadcastQueueModernImplTest";
+public final class BroadcastQueueImplTest extends BaseBroadcastQueueTest {
+ private static final String TAG = "BroadcastQueueImplTest";
private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
@@ -109,7 +109,7 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
@Mock BroadcastProcessQueue mQueue3;
@Mock BroadcastProcessQueue mQueue4;
- BroadcastQueueModernImpl mImpl;
+ BroadcastQueueImpl mImpl;
BroadcastProcessQueue mHead;
@@ -121,7 +121,7 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
mConstants.DELAY_NORMAL_MILLIS = 10_000;
mConstants.DELAY_CACHED_MILLIS = 120_000;
- mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+ mImpl = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(),
mConstants, mConstants, mSkipPolicy, mEmptyHistory);
mAms.setBroadcastQueueForTest(mImpl);
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 db6aeebecce7..ad35b25a0d74 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -233,7 +233,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
}).when(mAms).registerUidObserver(any(), anyInt(),
eq(ActivityManager.PROCESS_STATE_TOP), any());
- mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+ mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(),
mConstants, mConstants, mSkipPolicy, mEmptyHistory);
mAms.setBroadcastQueueForTest(mQueue);
mQueue.start(mContext.getContentResolver());
@@ -454,7 +454,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
private void assertHealth() {
// If this fails, it'll throw a clear reason message
- ((BroadcastQueueModernImpl) mQueue).assertHealthLocked();
+ ((BroadcastQueueImpl) mQueue).assertHealthLocked();
}
private static Map<String, Object> asMap(Bundle bundle) {
@@ -1953,7 +1953,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
withPriority(receiverGreenA, 5))));
waitForIdle();
- // In the modern queue, we don't end up replacing the old broadcast to
+ // In the broadcast queue, we don't end up replacing the old broadcast to
// avoid creating priority inversion and so the process will receive
// both the old and new broadcasts.
verifyScheduleRegisteredReceiver(times(3), receiverGreenApp, airplane);
@@ -2235,7 +2235,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest {
}
waitForIdle();
- // Modern stack requests once each time we promote a process to
+ // The broadcast queue requests once each time we promote a process to
// running; we promote "green" twice, and "blue" and "yellow" once
final int expectedTimes = 4;
verify(mAms, times(expectedTimes))
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index bc81feb3f7c7..164eec6fbc49 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -34,6 +34,8 @@ import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.NetworkRegistrationInfo;
import android.util.AtomicFile;
import android.util.Log;
@@ -46,6 +48,7 @@ import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerStats;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -61,14 +64,22 @@ import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.TimeZone;
/**
* Test BatteryStatsHistory.
*/
@RunWith(AndroidJUnit4.class)
+@EnableFlags({com.android.server.power.optimization.Flags
+ .FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED})
public class BatteryStatsHistoryTest {
private static final String TAG = "BatteryStatsHistoryTest";
+
private static final int MAX_HISTORY_BUFFER_SIZE = 1024;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final Parcel mHistoryBuffer = Parcel.obtain();
private File mSystemDir;
private File mHistoryDir;
@@ -98,15 +109,18 @@ public class BatteryStatsHistoryTest {
mHistoryDir.delete();
mClock.realtime = 123;
+ mClock.currentTime = 1743645660000L; // 2025-04-03, 2:01:00 AM
mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32768,
MAX_HISTORY_BUFFER_SIZE, mStepDetailsCalculator, mClock, mMonotonicClock, mTracer,
mEventLogger);
+ mHistory.forceRecordAllHistory();
+ mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false);
when(mStepDetailsCalculator.getHistoryStepDetails())
.thenReturn(new BatteryStats.HistoryStepDetails());
- mHistoryPrinter = new BatteryStats.HistoryPrinter();
+ mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"));
}
@Test
@@ -145,8 +159,6 @@ public class BatteryStatsHistoryTest {
@Test
public void testAtraceExcludedState() {
- mHistory.forceRecordAllHistory();
-
Mockito.when(mTracer.tracingEnabled()).thenReturn(true);
mHistory.recordStateStartEvent(mClock.elapsedRealtime(),
@@ -354,8 +366,6 @@ public class BatteryStatsHistoryTest {
}
private void prepareMultiFileHistory() {
- mHistory.forceRecordAllHistory();
-
mClock.realtime = 1000;
mClock.uptime = 1000;
mHistory.recordEvent(mClock.realtime, mClock.uptime,
@@ -428,7 +438,8 @@ public class BatteryStatsHistoryTest {
powerStats.uidStats.put(300, new long[]{400, 500});
powerStats.uidStats.put(600, new long[]{700, 800});
- mHistory.recordPowerStats(200, 200, powerStats);
+ mClock.advance(200);
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
BatteryStats.HistoryItem item;
@@ -437,7 +448,7 @@ public class BatteryStatsHistoryTest {
assertThat(item = iterator.next()).isNotNull();
String dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+200ms");
+ assertThat(dump).contains("04-03 02:01:00.200");
assertThat(dump).contains("duration=100");
assertThat(dump).contains("foo=[200]");
assertThat(dump).contains("300: [400, 500]");
@@ -446,49 +457,49 @@ public class BatteryStatsHistoryTest {
@Test
public void testNrState_dump() {
- mHistory.forceRecordAllHistory();
- mHistory.startRecordingHistory(0, 0, /* reset */ true);
mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
1234);
- mHistory.recordNrStateChangeEvent(200, 200,
+ mClock.advance(200);
+ mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
NetworkRegistrationInfo.NR_STATE_RESTRICTED);
- mHistory.recordNrStateChangeEvent(300, 300,
+ mClock.advance(100);
+ mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED);
- mHistory.recordNrStateChangeEvent(400, 400,
+ mClock.advance(100);
+ mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
NetworkRegistrationInfo.NR_STATE_CONNECTED);
- mHistory.recordNrStateChangeEvent(500, 500,
+ mClock.advance(100);
+ mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
NetworkRegistrationInfo.NR_STATE_NONE);
BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
- BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+ BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
assertThat(item = iterator.next()).isNotNull();
String dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+200ms");
+ assertThat(dump).contains("04-03 02:01:00.200");
assertThat(dump).contains("nr_state=restricted");
assertThat(item = iterator.next()).isNotNull();
dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+300ms");
+ assertThat(dump).contains("04-03 02:01:00.300");
assertThat(dump).contains("nr_state=not_restricted");
assertThat(item = iterator.next()).isNotNull();
dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+400ms");
+ assertThat(dump).contains("04-03 02:01:00.400");
assertThat(dump).contains("nr_state=connected");
assertThat(item = iterator.next()).isNotNull();
dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+500ms");
+ assertThat(dump).contains("04-03 02:01:00.500");
assertThat(dump).contains("nr_state=none");
}
@Test
public void testNrState_checkin() {
- mHistory.forceRecordAllHistory();
- mHistory.startRecordingHistory(0, 0, /* reset */ true);
mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
1234);
@@ -502,7 +513,7 @@ public class BatteryStatsHistoryTest {
NetworkRegistrationInfo.NR_STATE_NONE);
BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
- BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+ BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
assertThat(item = iterator.next()).isNotNull();
@@ -633,10 +644,17 @@ public class BatteryStatsHistoryTest {
@Test
public void recordProcStateChange() {
- mHistory.recordProcessStateChange(200, 200, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND);
- mHistory.recordProcessStateChange(300, 300, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ mClock.advance(200);
+ mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+ mClock.advance(100);
+ mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+
+ mClock.advance(100);
// Large UID, > 0xFFFFFF
- mHistory.recordProcessStateChange(400, 400,
+ mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime,
UserHandle.getUid(777, Process.LAST_ISOLATED_UID),
BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
@@ -647,17 +665,17 @@ public class BatteryStatsHistoryTest {
assertThat(item = iterator.next()).isNotNull();
String dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+200ms");
+ assertThat(dump).contains("04-03 02:01:00.200");
assertThat(dump).contains("procstate: 42: bg");
assertThat(item = iterator.next()).isNotNull();
dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+300ms");
+ assertThat(dump).contains("04-03 02:01:00.300");
assertThat(dump).contains("procstate: 42: fg");
assertThat(item = iterator.next()).isNotNull();
dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+400ms");
+ assertThat(dump).contains("04-03 02:01:00.400");
assertThat(dump).contains("procstate: u777i999: fgs");
}
@@ -672,7 +690,6 @@ public class BatteryStatsHistoryTest {
@Test
public void getMonotonicHistorySize() {
long lastHistorySize = mHistory.getMonotonicHistorySize();
- mHistory.forceRecordAllHistory();
mClock.realtime = 1000;
mClock.uptime = 1000;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java
index 5e57cc36797b..215ac409007e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java
@@ -40,4 +40,13 @@ public class MockClock extends Clock {
public long currentTimeMillis() {
return currentTime;
}
+
+ /**
+ * Advances the clock by the given number of milliseconds.
+ */
+ public void advance(long milliseconds) {
+ realtime += milliseconds;
+ uptime += milliseconds;
+ currentTime += milliseconds;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 854bda03f18d..51706d72cb35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -572,7 +572,6 @@ public class BackgroundActivityStartControllerTests {
+ "inVisibleTask: false; "
+ "balAllowedByPiCreator: BSP.ALLOW_BAL; "
+ "balAllowedByPiCreatorWithHardening: BSP.ALLOW_BAL; "
- + "resultIfPiCreatorAllowsBal: null; "
+ "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ "hasRealCaller: true; "
+ "isCallForResult: false; "
@@ -589,7 +588,6 @@ public class BackgroundActivityStartControllerTests {
+ "originatingPendingIntent: null; "
+ "realCallerApp: null; "
+ "balAllowedByPiSender: BSP.ALLOW_BAL; "
- + "resultIfPiSenderAllowsBal: null; "
+ "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ "balRequireOptInByPendingIntentCreator: true; "
+ "balDontBringExistingBackgroundTaskStackToFg: true]");
@@ -677,7 +675,6 @@ public class BackgroundActivityStartControllerTests {
+ "inVisibleTask: false; "
+ "balAllowedByPiCreator: BSP.NONE; "
+ "balAllowedByPiCreatorWithHardening: BSP.NONE; "
- + "resultIfPiCreatorAllowsBal: null; "
+ "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ "hasRealCaller: true; "
+ "isCallForResult: false; "
@@ -694,7 +691,6 @@ public class BackgroundActivityStartControllerTests {
+ "originatingPendingIntent: PendingIntentRecord; "
+ "realCallerApp: null; "
+ "balAllowedByPiSender: BSP.ALLOW_FGS; "
- + "resultIfPiSenderAllowsBal: null; "
+ "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
+ "balRequireOptInByPendingIntentCreator: true; "
+ "balDontBringExistingBackgroundTaskStackToFg: true]");