summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java17
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java41
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java25
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java11
-rw-r--r--core/api/current.txt3
-rw-r--r--core/api/system-current.txt9
-rw-r--r--core/java/android/service/quicksettings/Tile.java2
-rw-r--r--core/java/android/service/quicksettings/TileService.java7
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/public-staging.xml6
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java10
-rw-r--r--libs/WindowManager/Jetpack/Android.bp11
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java32
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java7
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java33
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java12
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java28
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java17
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java5
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-core-release.aarbin4568 -> 0 bytes
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin34072 -> 32970 bytes
-rw-r--r--libs/WindowManager/Shell/res/color/decor_title_color.xml (renamed from libs/WindowManager/Shell/res/color/decor_caption_title_color.xml)0
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml (renamed from libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml)0
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml (renamed from libs/WindowManager/Shell/res/drawable/decor_caption_title.xml)0
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml (renamed from libs/WindowManager/Shell/res/layout/caption_handle_menu.xml)2
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml (renamed from libs/WindowManager/Shell/res/layout/caption_window_decoration.xml)4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java3
-rw-r--r--media/java/android/media/AudioManager.java111
-rw-r--r--media/java/android/media/IAudioService.aidl7
-rw-r--r--media/java/android/media/IDevicesForAttributesCallback.aidl33
-rw-r--r--media/jni/android_media_tv_Tuner.cpp18
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml6
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java179
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java20
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java77
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java12
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml7
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values-sw600dp/dmiens.xml22
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values-sw720dp-land/dmiens.xml23
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values-sw720dp/dmiens.xml23
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java13
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java24
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml6
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/styles.xml6
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SystemUI/docs/modern-architecture.pngbin0 -> 23573 bytes
-rw-r--r--packages/SystemUI/docs/status-bar-data-pipeline.md261
-rw-r--r--packages/SystemUI/docs/status-bar-mobile-pipeline.pngbin0 -> 50983 bytes
-rw-r--r--packages/SystemUI/docs/status-bar-pipeline.pngbin0 -> 89119 bytes
-rw-r--r--packages/SystemUI/docs/status-bar.pngbin0 -> 19725 bytes
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt6
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java1
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java20
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java115
-rw-r--r--services/core/java/com/android/server/infra/AbstractMasterSystemService.java23
-rw-r--r--services/core/java/com/android/server/infra/AbstractPerUserSystemService.java9
-rw-r--r--services/core/java/com/android/server/power/ShutdownCheckPoints.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/job/JobStoreTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java44
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java2
69 files changed, 1170 insertions, 269 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 8defa162b96e..4e52ed352981 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -4303,14 +4303,13 @@ public class JobSchedulerService extends com.android.server.SystemService
int getStorageSeq() {
synchronized (mLock) {
- return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
+ return mStorageController.getTracker().getSeq();
}
}
boolean getStorageNotLow() {
synchronized (mLock) {
- return mStorageController != null
- ? mStorageController.getTracker().isStorageNotLow() : false;
+ return mStorageController.getTracker().isStorageNotLow();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 882704949707..53c56e790831 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -832,8 +832,8 @@ public final class JobStore {
private void writeConstraintsToXml(TypedXmlSerializer out, JobStatus jobStatus)
throws IOException {
out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
+ final JobInfo job = jobStatus.getJob();
if (jobStatus.hasConnectivityConstraint()) {
- final JobInfo job = jobStatus.getJob();
final NetworkRequest network = jobStatus.getJob().getRequiredNetwork();
out.attribute(null, "net-capabilities-csv", intArrayToString(
network.getCapabilities()));
@@ -854,16 +854,16 @@ public final class JobStore {
job.getMinimumNetworkChunkBytes());
}
}
- if (jobStatus.hasIdleConstraint()) {
+ if (job.isRequireDeviceIdle()) {
out.attribute(null, "idle", Boolean.toString(true));
}
- if (jobStatus.hasChargingConstraint()) {
+ if (job.isRequireCharging()) {
out.attribute(null, "charging", Boolean.toString(true));
}
- if (jobStatus.hasBatteryNotLowConstraint()) {
+ if (job.isRequireBatteryNotLow()) {
out.attribute(null, "battery-not-low", Boolean.toString(true));
}
- if (jobStatus.hasStorageNotLowConstraint()) {
+ if (job.isRequireStorageNotLow()) {
out.attribute(null, "storage-not-low", Boolean.toString(true));
}
out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
index 36a26f07e08d..0a305a263363 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
@@ -273,6 +273,13 @@ class PendingJobQueue {
return Integer.compare(job2.overrideState, job1.overrideState);
}
+ final boolean job1UI = job1.getJob().isUserInitiated();
+ final boolean job2UI = job2.getJob().isUserInitiated();
+ if (job1UI != job2UI) {
+ // Attempt to run user-initiated jobs ahead of all other jobs.
+ return job1UI ? -1 : 1;
+ }
+
final boolean job1EJ = job1.isRequestedExpeditedJob();
final boolean job2EJ = job2.isRequestedExpeditedJob();
if (job1EJ != job2EJ) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index b0b6a01f6281..0b875ccffb21 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -96,10 +96,16 @@ public final class JobStatus {
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
- static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
- static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
- static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
- static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static final int CONSTRAINT_BATTERY_NOT_LOW =
+ JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static final int CONSTRAINT_STORAGE_NOT_LOW =
+ JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
public static final int CONSTRAINT_TIMING_DELAY = 1 << 31;
public static final int CONSTRAINT_DEADLINE = 1 << 30;
public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
@@ -1820,7 +1826,8 @@ public final class JobStatus {
* separately from the job's explicitly requested constraints and MUST be satisfied before
* the job can run if the app doesn't have quota.
*/
- private void addDynamicConstraints(int constraints) {
+ @VisibleForTesting
+ public void addDynamicConstraints(int constraints) {
if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
// Quota should never be used as a dynamic constraint.
Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index b84c8a41af1c..2b5920912718 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -164,8 +164,9 @@ class Agent {
}
@GuardedBy("mLock")
- private boolean isAffordableLocked(long balance, long price, long ctp) {
- return balance >= price && mScribe.getRemainingConsumableCakesLocked() >= ctp;
+ private boolean isAffordableLocked(long balance, long price, long stockLimitHonoringCtp) {
+ return balance >= price
+ && mScribe.getRemainingConsumableCakesLocked() >= stockLimitHonoringCtp;
}
@GuardedBy("mLock")
@@ -303,7 +304,8 @@ class Agent {
note.recalculateCosts(economicPolicy, userId, pkgName);
final boolean isAffordable = isVip
|| isAffordableLocked(newBalance,
- note.getCachedModifiedPrice(), note.getCtp());
+ note.getCachedModifiedPrice(),
+ note.getStockLimitHonoringCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -339,7 +341,7 @@ class Agent {
note.recalculateCosts(economicPolicy, userId, pkgName);
final boolean isAffordable = isVip
|| isAffordableLocked(newBalance,
- note.getCachedModifiedPrice(), note.getCtp());
+ note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -403,7 +405,8 @@ class Agent {
note.recalculateCosts(economicPolicy, userId, pkgName);
final boolean isAffordable = isVip
|| isAffordableLocked(newBalance,
- note.getCachedModifiedPrice(), note.getCtp());
+ note.getCachedModifiedPrice(),
+ note.getStockLimitHonoringCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -541,7 +544,7 @@ class Agent {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
final boolean isAffordable = isVip
|| isAffordableLocked(newBalance,
- note.getCachedModifiedPrice(), note.getCtp());
+ note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -882,7 +885,7 @@ class Agent {
mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE)
? price : Math.min(mUpperThreshold, price);
}
- final long ctp = note.getCtp();
+ final long ctp = note.getStockLimitHonoringCtp();
if (ctp <= mRemainingConsumableCredits) {
mCtpThreshold = Math.max(mCtpThreshold, ctp);
}
@@ -1119,7 +1122,7 @@ class Agent {
note.recalculateCosts(economicPolicy, userId, pkgName);
note.setNewAffordability(isVip
|| isAffordableLocked(getBalanceLocked(userId, pkgName),
- note.getCachedModifiedPrice(), note.getCtp()));
+ note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp()));
mIrs.postAffordabilityChanged(userId, pkgName, note);
// Update ongoing alarm
scheduleBalanceCheckLocked(userId, pkgName);
@@ -1146,7 +1149,7 @@ class Agent {
static final class ActionAffordabilityNote {
private final EconomyManagerInternal.ActionBill mActionBill;
private final EconomyManagerInternal.AffordabilityChangeListener mListener;
- private long mCtp;
+ private long mStockLimitHonoringCtp;
private long mModifiedPrice;
private boolean mIsAffordable;
@@ -1185,29 +1188,34 @@ class Agent {
return mModifiedPrice;
}
- private long getCtp() {
- return mCtp;
+ /** Returns the cumulative CTP of actions in this note that respect the stock limit. */
+ private long getStockLimitHonoringCtp() {
+ return mStockLimitHonoringCtp;
}
@VisibleForTesting
void recalculateCosts(@NonNull EconomicPolicy economicPolicy,
int userId, @NonNull String pkgName) {
long modifiedPrice = 0;
- long ctp = 0;
+ long stockLimitHonoringCtp = 0;
final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
mActionBill.getAnticipatedActions();
for (int i = 0; i < anticipatedActions.size(); ++i) {
final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
+ final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId);
final EconomicPolicy.Cost actionCost =
economicPolicy.getCostOfAction(aa.actionId, userId, pkgName);
modifiedPrice += actionCost.price * aa.numInstantaneousCalls
+ actionCost.price * (aa.ongoingDurationMs / 1000);
- ctp += actionCost.costToProduce * aa.numInstantaneousCalls
- + actionCost.costToProduce * (aa.ongoingDurationMs / 1000);
+ if (action.respectsStockLimit) {
+ stockLimitHonoringCtp +=
+ actionCost.costToProduce * aa.numInstantaneousCalls
+ + actionCost.costToProduce * (aa.ongoingDurationMs / 1000);
+ }
}
mModifiedPrice = modifiedPrice;
- mCtp = ctp;
+ mStockLimitHonoringCtp = stockLimitHonoringCtp;
}
boolean isCurrentlyAffordable() {
@@ -1267,7 +1275,8 @@ class Agent {
final ActionAffordabilityNote note =
actionAffordabilityNotes.valueAt(i);
final boolean isAffordable = isVip || isAffordableLocked(
- newBalance, note.getCachedModifiedPrice(), note.getCtp());
+ newBalance, note.getCachedModifiedPrice(),
+ note.getStockLimitHonoringCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
mIrs.postAffordabilityChanged(userId, pkgName, note);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index 46338fa69eb0..a6d064c76044 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -262,12 +262,17 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES);
+ // Apps must hold the SCHEDULE_EXACT_ALARM or USE_EXACT_ALARMS permission in order to use
+ // exact alarms. Since the user has the option of granting/revoking the permission, we can
+ // be a little lenient on the action cost checks and only stop the action if the app has
+ // run out of credits, and not when the system has run out of stock.
mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES),
- exactAllowWhileIdleWakeupBasePrice));
+ exactAllowWhileIdleWakeupBasePrice,
+ /* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_EXACT,
new Action(ACTION_ALARM_WAKEUP_EXACT,
getConstantAsCake(mParser, properties,
@@ -275,7 +280,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES),
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
- DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES)));
+ DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES),
+ /* respectsStockLimit */ false));
final long inexactAllowWhileIdleWakeupBasePrice =
getConstantAsCake(mParser, properties,
@@ -287,7 +293,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES),
- inexactAllowWhileIdleWakeupBasePrice));
+ inexactAllowWhileIdleWakeupBasePrice,
+ /* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
new Action(ACTION_ALARM_WAKEUP_INEXACT,
getConstantAsCake(mParser, properties,
@@ -295,7 +302,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES),
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
- DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES)));
+ DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES),
+ /* respectsStockLimit */ false));
final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
@@ -305,7 +313,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES),
- exactAllowWhileIdleNonWakeupBasePrice));
+ exactAllowWhileIdleNonWakeupBasePrice,
+ /* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
new Action(ACTION_ALARM_NONWAKEUP_EXACT,
@@ -314,7 +323,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES),
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
- DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES)));
+ DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES),
+ /* respectsStockLimit */ false));
final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
@@ -342,7 +352,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES),
getConstantAsCake(mParser, properties,
KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
- DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES)));
+ DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES),
+ /* respectsStockLimit */ false));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
getConstantAsCake(mParser, properties,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index b52f6f11b3bf..a4043dd8ba78 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -149,11 +149,22 @@ public abstract class EconomicPolicy {
* the action unless a modifier lowers the cost to produce.
*/
public final long basePrice;
+ /**
+ * Whether the remaining stock limit affects an app's ability to perform this action.
+ * If {@code false}, then the action can be performed, even if the cost is higher
+ * than the remaining stock. This does not affect checking against an app's balance.
+ */
+ public final boolean respectsStockLimit;
Action(int id, long costToProduce, long basePrice) {
+ this(id, costToProduce, basePrice, true);
+ }
+
+ Action(int id, long costToProduce, long basePrice, boolean respectsStockLimit) {
this.id = id;
this.costToProduce = costToProduce;
this.basePrice = basePrice;
+ this.respectsStockLimit = respectsStockLimit;
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 1b0c5c20d2e3..326a8e781a60 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40318,12 +40318,14 @@ package android.service.quicksettings {
public final class Tile implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.app.PendingIntent getActivityLaunchForClick();
method public CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public CharSequence getLabel();
method public int getState();
method @Nullable public CharSequence getStateDescription();
method @Nullable public CharSequence getSubtitle();
+ method public void setActivityLaunchForClick(@Nullable android.app.PendingIntent);
method public void setContentDescription(CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(CharSequence);
@@ -40352,6 +40354,7 @@ package android.service.quicksettings {
method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
method public final void showDialog(android.app.Dialog);
method public final void startActivityAndCollapse(android.content.Intent);
+ method public final void startActivityAndCollapse(@NonNull android.app.PendingIntent);
method public final void unlockAndRun(Runnable);
field public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bc15d3efa8bf..b4594e00e5c6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -393,6 +393,8 @@ package android {
}
public static final class R.bool {
+ field public static final int config_enableDefaultNotes;
+ field public static final int config_enableDefaultNotesForWorkProfile;
field public static final int config_enableQrCodeScannerOnLockScreen = 17891336; // 0x1110008
field public static final int config_safetyProtectionEnabled;
field public static final int config_sendPackageName = 17891328; // 0x1110000
@@ -429,6 +431,7 @@ package android {
field public static final int config_defaultCallRedirection = 17039397; // 0x1040025
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
+ field public static final int config_defaultNotes;
field public static final int config_defaultSms = 17039396; // 0x1040024
field public static final int config_devicePolicyManagement = 17039421; // 0x104003d
field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
@@ -6604,6 +6607,7 @@ package android.media {
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addAssistantServicesUids(@NonNull int[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public void addOnDevicesForAttributesChangedListener(@NonNull android.media.AudioAttributes, @NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnDevicesForAttributesChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnNonDefaultDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnNonDefaultDevicesForStrategyChangedListener) throws java.lang.SecurityException;
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
@@ -6644,6 +6648,7 @@ package android.media {
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removeDeviceAsNonDefaultForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public void removeOnDevicesForAttributesChangedListener(@NonNull android.media.AudioManager.OnDevicesForAttributesChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnNonDefaultDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnNonDefaultDevicesForStrategyChangedListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
@@ -6701,6 +6706,10 @@ package android.media {
field public static final int EVENT_TIMEOUT = 2; // 0x2
}
+ public static interface AudioManager.OnDevicesForAttributesChangedListener {
+ method public void onDevicesForAttributesChanged(@NonNull android.media.AudioAttributes, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+ }
+
public static interface AudioManager.OnNonDefaultDevicesForStrategyChangedListener {
method public void onNonDefaultDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
}
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 289b0e055f53..910fc44988b2 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -227,7 +227,6 @@ public final class Tile implements Parcelable {
/**
* Gets the Activity {@link PendingIntent} to be launched when the tile is clicked.
- * @hide
*/
@Nullable
public PendingIntent getActivityLaunchForClick() {
@@ -243,7 +242,6 @@ public final class Tile implements Parcelable {
* (This is the default behavior if this method is never called.)
* @param pendingIntent a PendingIntent for an activity to be launched onclick, or {@code null}
* to handle the clicks in the `TileService`.
- * @hide
*/
public void setActivityLaunchForClick(@Nullable PendingIntent pendingIntent) {
if (pendingIntent != null && !pendingIntent.isActivity()) {
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 506b3b81eb9a..7b6ff975e8df 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -15,6 +15,7 @@
*/
package android.service.quicksettings;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -41,6 +42,8 @@ import android.view.WindowManager;
import com.android.internal.R;
+import java.util.Objects;
+
/**
* A TileService provides the user a tile that can be added to Quick Settings.
* Quick Settings is a space provided that allows the user to change settings and
@@ -341,9 +344,9 @@ public class TileService extends Service {
* Will collapse Quick Settings after launching.
*
* @param pendingIntent A PendingIntent for an Activity to be launched immediately.
- * @hide
*/
- public void startActivityAndCollapse(PendingIntent pendingIntent) {
+ public final void startActivityAndCollapse(@NonNull PendingIntent pendingIntent) {
+ Objects.requireNonNull(pendingIntent);
try {
mService.startActivity(mTileToken, pendingIntent);
} catch (RemoteException e) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e031e5f75603..a7c48f3a4e8f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5977,7 +5977,7 @@
<permission android:name="android.permission.START_VIEW_PERMISSION_USAGE"
android:label="@string/permlab_startViewPermissionUsage"
android:description="@string/permdesc_startViewPermissionUsage"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|module" />
<!--
@SystemApi
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 184f86968e51..23103679f440 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2135,6 +2135,12 @@
<string name="config_defaultAutomotiveNavigation" translatable="false"></string>
<!-- The name of the package that will hold the system wear health service role. -->
<string name="config_systemWearHealthService" translatable="false"></string>
+ <!-- The name of the package that will hold the default notes role. -->
+ <string name="config_defaultNotes" translatable="false"></string>
+ <!-- Whether the default notes role should be enabled. -->
+ <bool name="config_enableDefaultNotes">false</bool>
+ <!-- Whether the default notes role for work profile should be enabled. -->
+ <bool name="config_enableDefaultNotesForWorkProfile">false</bool>
<!-- The name of the package that will handle updating the device management role. -->
<string name="config_devicePolicyManagementUpdater" translatable="false"></string>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 97feaace7e59..dfd4d9a18271 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -139,6 +139,8 @@
<staging-public-group type="string" first-id="0x01cb0000">
<!-- @hide @SystemApi -->
<public name="config_systemWearHealthService" />
+ <!-- @hide @SystemApi -->
+ <public name="config_defaultNotes" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01ca0000">
@@ -182,6 +184,10 @@
<staging-public-group type="bool" first-id="0x01be0000">
<!-- @hide @SystemApi -->
<public name="config_safetyProtectionEnabled" />
+ <!-- @hide @SystemApi -->
+ <public name="config_enableDefaultNotes" />
+ <!-- @hide @SystemApi -->
+ <public name="config_enableDefaultNotesForWorkProfile" />
</staging-public-group>
<staging-public-group type="fraction" first-id="0x01bd0000">
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 9947d34495ab..c55a781ce2a4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -38,6 +38,7 @@ import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
+import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
@@ -221,7 +222,14 @@ public class AndroidKeyStoreProvider extends Provider {
}
final byte[] x509PublicCert = metadata.certificate;
- PublicKey publicKey = AndroidKeyStoreSpi.toCertificate(x509PublicCert).getPublicKey();
+ final X509Certificate parsedX509Certificate =
+ AndroidKeyStoreSpi.toCertificate(x509PublicCert);
+ if (parsedX509Certificate == null) {
+ throw new UnrecoverableKeyException("Failed to parse the X.509 certificate containing"
+ + " the public key. This likely indicates a hardware problem.");
+ }
+
+ PublicKey publicKey = parsedX509Certificate.getPublicKey();
String jcaKeyAlgorithm = publicKey.getAlgorithm();
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index a5b192cd7ceb..dc4b5636a246 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -63,12 +63,6 @@ android_library_import {
sdk_version: "current",
}
-android_library_import {
- name: "window-extensions-core",
- aars: ["window-extensions-core-release.aar"],
- sdk_version: "current",
-}
-
java_library {
name: "androidx.window.extensions",
srcs: [
@@ -76,10 +70,7 @@ java_library {
"src/androidx/window/util/**/*.java",
"src/androidx/window/common/**/*.java",
],
- static_libs: [
- "window-extensions",
- "window-extensions-core",
- ],
+ static_libs: ["window-extensions"],
installable: true,
sdk_version: "core_platform",
system_ext_specific: true,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 54474ae0f966..ce7d695beb2a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -75,10 +75,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
-import androidx.window.extensions.WindowExtensionsImpl;
import androidx.window.extensions.WindowExtensionsProvider;
-import androidx.window.extensions.core.util.function.Consumer;
-import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
@@ -89,6 +86,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Main controller class that manages split states and presentation.
@@ -114,7 +112,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* A developer-defined {@link SplitAttributes} calculator to compute the current
* {@link SplitAttributes} with the current device and window states.
- * It is registered via {@link #setSplitAttributesCalculator(Function)}
+ * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)}
* and unregistered via {@link #clearSplitAttributesCalculator()}.
* This is called when:
* <ul>
@@ -127,7 +125,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@GuardedBy("mLock")
@Nullable
- private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;
+ private SplitAttributesCalculator mSplitAttributesCalculator;
/**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
@@ -140,7 +138,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
/** Callback to Jetpack to notify about changes to split states. */
- @GuardedBy("mLock")
@Nullable
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
@@ -174,8 +171,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
mWindowLayoutComponent.addFoldingStateChangedCallback(new FoldingFeatureListener());
}
- private class FoldingFeatureListener
- implements java.util.function.Consumer<List<CommonFoldingFeature>> {
+ private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
@Override
public void accept(List<CommonFoldingFeature> foldingFeatures) {
synchronized (mLock) {
@@ -216,8 +212,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void setSplitAttributesCalculator(
- @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) {
+ public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
synchronized (mLock) {
mSplitAttributesCalculator = calculator;
}
@@ -232,7 +227,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
@Nullable
- Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
+ SplitAttributesCalculator getSplitAttributesCalculator() {
return mSplitAttributesCalculator;
}
@@ -245,22 +240,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Registers the split organizer callback to notify about changes to active splits.
- * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
- * {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
*/
- @Deprecated
@Override
- public void setSplitInfoCallback(
- @NonNull java.util.function.Consumer<List<SplitInfo>> callback) {
- Consumer<List<SplitInfo>> oemConsumer = callback::accept;
- setSplitInfoCallback(oemConsumer);
- }
-
- /**
- * Registers the split organizer callback to notify about changes to active splits.
- * @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
- */
- public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
+ public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
synchronized (mLock) {
mEmbeddingCallback = callback;
updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 41580a097ec4..9db9f8788190 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -42,11 +42,11 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
+import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams;
import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -522,8 +522,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
- final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
- mController.getSplitAttributesCalculator();
+ final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator();
final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics);
if (calculator == null) {
@@ -539,7 +538,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
taskWindowMetrics, taskConfiguration, defaultSplitAttributes,
isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag());
- final SplitAttributes splitAttributes = calculator.apply(params);
+ final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params);
return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 32b915cb49f7..84b2bfc38559 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -44,7 +44,6 @@ import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.RawFoldingFeatureProducer;
-import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.util.DataProducer;
import java.util.ArrayList;
@@ -52,6 +51,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Reference implementation of androidx.window.extensions.layout OEM interface for use with
@@ -80,10 +80,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
new ArrayMap<>();
- @GuardedBy("mLock")
- private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
- mJavaToExtConsumers = new ArrayMap<>();
-
public WindowLayoutComponentImpl(@NonNull Context context) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
@@ -94,8 +90,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
/** Registers to listen to {@link CommonFoldingFeature} changes */
- public void addFoldingStateChangedCallback(
- java.util.function.Consumer<List<CommonFoldingFeature>> consumer) {
+ public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
synchronized (mLock) {
mFoldingFeatureProducer.addDataChangedCallback(consumer);
}
@@ -109,17 +104,13 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*/
@Override
public void addWindowLayoutInfoListener(@NonNull Activity activity,
- @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
- final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
- synchronized (mLock) {
- mJavaToExtConsumers.put(consumer, extConsumer);
- }
- addWindowLayoutInfoListener(activity, extConsumer);
+ @NonNull Consumer<WindowLayoutInfo> consumer) {
+ addWindowLayoutInfoListener((Context) activity, consumer);
}
/**
- * Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but
- * takes a UI Context as a parameter.
+ * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context
+ * as a parameter.
*
* Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all
* consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo}
@@ -160,18 +151,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
}
- @Override
- public void removeWindowLayoutInfoListener(
- @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
- final Consumer<WindowLayoutInfo> extConsumer;
- synchronized (mLock) {
- extConsumer = mJavaToExtConsumers.remove(consumer);
- }
- if (extConsumer != null) {
- removeWindowLayoutInfoListener(extConsumer);
- }
- }
-
/**
* Removes a listener no longer interested in receiving updates.
*
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index 13a2c78d463e..d189ae2cf72e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -22,6 +22,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.window.extensions.embedding.SplitAttributes;
import org.junit.Before;
import org.junit.Test;
@@ -53,4 +54,15 @@ public class WindowExtensionsTest {
public void testGetActivityEmbeddingComponent() {
assertThat(mExtensions.getActivityEmbeddingComponent()).isNotNull();
}
+
+ @Test
+ public void testSplitAttributes_default() {
+ // Make sure the default value in the extensions aar.
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder().build();
+ assertThat(splitAttributes.getLayoutDirection())
+ .isEqualTo(SplitAttributes.LayoutDirection.LOCALE);
+ assertThat(splitAttributes.getSplitType())
+ .isEqualTo(new SplitAttributes.SplitType.RatioSplitType(0.5f));
+ assertThat(splitAttributes.getAnimationBackgroundColor()).isEqualTo(0);
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index 459ec9f89c4a..2f92a577baa2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -34,11 +34,9 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Pair;
-import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerToken;
-import androidx.window.extensions.core.util.function.Predicate;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -109,7 +107,7 @@ public class EmbeddingTestUtils {
static SplitRule createSplitRule(@NonNull Activity primaryActivity,
@NonNull Intent secondaryIntent, boolean clearTop) {
final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
- return createSplitPairRuleBuilder(
+ return new SplitPairRule.Builder(
activityPair -> false,
targetPair::equals,
w -> true)
@@ -146,7 +144,7 @@ public class EmbeddingTestUtils {
@NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
int finishSecondaryWithPrimary, boolean clearTop) {
final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
- return createSplitPairRuleBuilder(
+ return new SplitPairRule.Builder(
targetPair::equals,
activityIntentPair -> false,
w -> true)
@@ -225,26 +223,4 @@ public class EmbeddingTestUtils {
displayFeatures.add(foldingFeature);
return new WindowLayoutInfo(displayFeatures);
}
-
- static ActivityRule.Builder createActivityBuilder(
- @NonNull Predicate<Activity> activityPredicate,
- @NonNull Predicate<Intent> intentPredicate) {
- return new ActivityRule.Builder(activityPredicate, intentPredicate);
- }
-
- static SplitPairRule.Builder createSplitPairRuleBuilder(
- @NonNull Predicate<Pair<Activity, Activity>> activitiesPairPredicate,
- @NonNull Predicate<Pair<Activity, Intent>> activityIntentPairPredicate,
- @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
- return new SplitPairRule.Builder(activitiesPairPredicate, activityIntentPairPredicate,
- windowMetricsPredicate);
- }
-
- static SplitPlaceholderRule.Builder createSplitPlaceholderRuleBuilder(
- @NonNull Intent placeholderIntent, @NonNull Predicate<Activity> activityPredicate,
- @NonNull Predicate<Intent> intentPredicate,
- @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
- return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
- intentPredicate, windowMetricsPredicate);
- }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index f9cbb5c65877..3cc31f9761c1 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -34,11 +34,8 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTR
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -432,7 +429,7 @@ public class SplitControllerTest {
@Test
public void testResolveStartActivityIntent_withoutLaunchingActivity() {
final Intent intent = new Intent();
- final ActivityRule expandRule = createActivityBuilder(r -> false, i -> i == intent)
+ final ActivityRule expandRule = new ActivityRule.Builder(r -> false, i -> i == intent)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1170,7 +1167,7 @@ public class SplitControllerTest {
@Test
public void testHasSamePresentation() {
- SplitPairRule splitRule1 = createSplitPairRuleBuilder(
+ SplitPairRule splitRule1 = new SplitPairRule.Builder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1178,7 +1175,7 @@ public class SplitControllerTest {
.setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
- SplitPairRule splitRule2 = createSplitPairRuleBuilder(
+ SplitPairRule splitRule2 = new SplitPairRule.Builder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1191,7 +1188,7 @@ public class SplitControllerTest {
SplitController.haveSamePresentation(splitRule1, splitRule2,
new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
- splitRule2 = createSplitPairRuleBuilder(
+ splitRule2 = new SplitPairRule.Builder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1355,7 +1352,7 @@ public class SplitControllerTest {
/** Setups a rule to always expand the given intent. */
private void setupExpandRule(@NonNull Intent expandIntent) {
- final ActivityRule expandRule = createActivityBuilder(r -> false, expandIntent::equals)
+ final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1363,7 +1360,7 @@ public class SplitControllerTest {
/** Setups a rule to always expand the given activity. */
private void setupExpandRule(@NonNull Activity expandActivity) {
- final ActivityRule expandRule = createActivityBuilder(expandActivity::equals, i -> false)
+ final ActivityRule expandRule = new ActivityRule.Builder(expandActivity::equals, i -> false)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1371,7 +1368,7 @@ public class SplitControllerTest {
/** Setups a rule to launch placeholder for the given activity. */
private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
- final SplitRule placeholderRule = createSplitPlaceholderRuleBuilder(PLACEHOLDER_INTENT,
+ final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT,
primaryActivity::equals, i -> false, w -> true)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index cbb6e31512db..6dae0a1086b3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -27,7 +27,6 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUND
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -474,7 +473,7 @@ public class SplitPresenterTest {
final Activity secondaryActivity = createMockActivity();
final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
- final SplitPairRule rule = createSplitPairRuleBuilder(pair ->
+ final SplitPairRule rule = new SplitPairRule.Builder(pair ->
pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
metrics -> true)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
@@ -492,7 +491,7 @@ public class SplitPresenterTest {
@Test
public void testComputeSplitAttributes() {
- final SplitPairRule splitPairRule = createSplitPairRuleBuilder(
+ final SplitPairRule splitPairRule = new SplitPairRule.Builder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS))
diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
deleted file mode 100644
index 4c0807441870..000000000000
--- a/libs/WindowManager/Jetpack/window-extensions-core-release.aar
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 6fd8d296733a..84ab4487feee 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml b/libs/WindowManager/Shell/res/color/decor_title_color.xml
index 1ecc13e4da38..1ecc13e4da38 100644
--- a/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml
+++ b/libs/WindowManager/Shell/res/color/decor_title_color.xml
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
index 416287d2cbb3..416287d2cbb3 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
index 416287d2cbb3..416287d2cbb3 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml
diff --git a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
index 582a11cfdb8e..8b4792acba3e 100644
--- a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
@@ -20,7 +20,7 @@ android:id="@+id/handle_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
-android:background="@drawable/decor_caption_menu_background">
+android:background="@drawable/desktop_mode_decor_menu_background">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/fullscreen_button"
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
index 51e634c17532..2a4cc02f0925 100644
--- a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
@@ -16,10 +16,10 @@
-->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/caption"
+ android:id="@+id/desktop_mode_caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/decor_caption_title">
+ android:background="@drawable/desktop_mode_decor_title">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/back_button"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index b500f5fb0155..b4301577ac10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -282,7 +282,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
public boolean onTouch(View v, MotionEvent e) {
boolean isDrag = false;
int id = v.getId();
- if (id != R.id.caption_handle && id != R.id.caption) {
+ if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
return false;
}
if (id == R.id.caption_handle) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 467f374f2110..9c2beb9c4b2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -132,7 +132,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
- mRelayoutParams.mLayoutResId = R.layout.caption_window_decoration;
+ mRelayoutParams.mLayoutResId = R.layout.desktop_mode_window_decor;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
mRelayoutParams.mCaptionWidthId = R.dimen.freeform_decor_caption_width;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
@@ -212,7 +212,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Sets up listeners when a new root view is created.
*/
private void setupRootView() {
- View caption = mResult.mRootView.findViewById(R.id.caption);
+ View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
caption.setOnTouchListener(mOnCaptionTouchListener);
View close = caption.findViewById(R.id.close_window);
close.setOnClickListener(mOnCaptionButtonClickListener);
@@ -243,7 +243,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
private void setCaptionVisibility(boolean visible) {
int v = visible ? View.VISIBLE : View.GONE;
- View captionView = mResult.mRootView.findViewById(R.id.caption);
+ View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
captionView.setVisibility(v);
if (!visible) closeHandleMenu();
}
@@ -265,7 +265,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void setButtonVisibility(boolean visible) {
int visibility = visible ? View.VISIBLE : View.GONE;
- View caption = mResult.mRootView.findViewById(R.id.caption);
+ View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
View back = caption.findViewById(R.id.back_button);
View close = caption.findViewById(R.id.close_window);
back.setVisibility(visibility);
@@ -304,7 +304,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
String namePrefix = "Caption Menu";
- mHandleMenu = addWindow(R.layout.caption_handle_menu, namePrefix, t,
+ mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t,
x - mResult.mDecorContainerOffsetX, y - mResult.mDecorContainerOffsetY,
width, height);
mSyncQueue.runInSync(transaction -> {
@@ -336,7 +336,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void closeHandleMenuIfNeeded(MotionEvent ev) {
if (isHandleMenuActive()) {
- if (!checkEventInCaptionView(ev, R.id.caption)) {
+ if (!checkEventInCaptionView(ev, R.id.desktop_mode_caption)) {
closeHandleMenu();
}
}
@@ -389,7 +389,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void checkClickEvent(MotionEvent ev) {
if (mResult.mRootView == null) return;
- View caption = mResult.mRootView.findViewById(R.id.caption);
+ View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
PointF inputPoint = offsetCaptionLocation(ev);
if (!isHandleMenuActive()) {
View handle = caption.findViewById(R.id.caption_handle);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index dd9ab9899e13..d4746ee49300 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -560,7 +560,8 @@ public class WindowDecorationTests extends ShellTestCase {
int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
String name = "Test Window";
WindowDecoration.AdditionalWindow additionalWindow =
- addWindow(R.layout.caption_handle_menu, name, mMockSurfaceControlAddWindowT,
+ addWindow(R.layout.desktop_mode_decor_handle_menu, name,
+ mMockSurfaceControlAddWindowT,
x - mRelayoutResult.mDecorContainerOffsetX,
y - mRelayoutResult.mDecorContainerOffsetY,
width, height);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a84c42af93c2..761edf6c6170 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5855,6 +5855,117 @@ public class AudioManager {
}
}
+ // Each listener corresponds to a unique callback stub because each listener can subscribe to
+ // different AudioAttributes.
+ private final ConcurrentHashMap<OnDevicesForAttributesChangedListener,
+ IDevicesForAttributesCallbackStub> mDevicesForAttributesListenerToStub =
+ new ConcurrentHashMap<>();
+
+ private static final class IDevicesForAttributesCallbackStub
+ extends IDevicesForAttributesCallback.Stub {
+ ListenerInfo<OnDevicesForAttributesChangedListener> mInfo;
+
+ IDevicesForAttributesCallbackStub(@NonNull OnDevicesForAttributesChangedListener listener,
+ @NonNull Executor executor) {
+ mInfo = new ListenerInfo<>(listener, executor);
+ }
+
+ public void register(boolean register, AudioAttributes attributes) {
+ try {
+ if (register) {
+ getService().addOnDevicesForAttributesChangedListener(attributes, this);
+ } else {
+ getService().removeOnDevicesForAttributesChangedListener(this);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onDevicesForAttributesChanged(AudioAttributes attributes, boolean forVolume,
+ List<AudioDeviceAttributes> devices) {
+ // forVolume is ignored. The case where it is `true` is not handled.
+ mInfo.mExecutor.execute(() ->
+ mInfo.mListener.onDevicesForAttributesChanged(
+ attributes, devices));
+ }
+ }
+
+ /**
+ * @hide
+ * Interface to be notified of when routing changes for the registered audio attributes.
+ */
+ @SystemApi
+ public interface OnDevicesForAttributesChangedListener {
+ /**
+ * Called on the listener to indicate that the audio devices for the given audio
+ * attributes have changed.
+ * @param attributes the {@link AudioAttributes} whose routing changed
+ * @param devices a list of newly routed audio devices
+ */
+ void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes,
+ @NonNull List<AudioDeviceAttributes> devices);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of routing changes for the given {@link AudioAttributes}.
+ * @param attributes the {@link AudioAttributes} to listen for routing changes
+ * @param executor
+ * @param listener
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public void addOnDevicesForAttributesChangedListener(@NonNull AudioAttributes attributes,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnDevicesForAttributesChangedListener listener) {
+ Objects.requireNonNull(attributes);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+
+ synchronized (mDevicesForAttributesListenerToStub) {
+ IDevicesForAttributesCallbackStub callbackStub =
+ mDevicesForAttributesListenerToStub.get(listener);
+
+ if (callbackStub == null) {
+ callbackStub = new IDevicesForAttributesCallbackStub(listener, executor);
+ mDevicesForAttributesListenerToStub.put(listener, callbackStub);
+ }
+
+ callbackStub.register(true, attributes);
+ }
+ }
+
+ /**
+ * @hide
+ * Removes a previously registered listener for being notified of routing changes for the given
+ * {@link AudioAttributes}.
+ * @param listener
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public void removeOnDevicesForAttributesChangedListener(
+ @NonNull OnDevicesForAttributesChangedListener listener) {
+ Objects.requireNonNull(listener);
+
+ synchronized (mDevicesForAttributesListenerToStub) {
+ IDevicesForAttributesCallbackStub callbackStub =
+ mDevicesForAttributesListenerToStub.get(listener);
+ if (callbackStub != null) {
+ callbackStub.register(false, null /* attributes */);
+ }
+
+ mDevicesForAttributesListenerToStub.remove(listener);
+ }
+ }
+
/**
* Get the audio devices that would be used for the routing of the given audio attributes.
* These are the devices anticipated to play sound from an {@link AudioTrack} created with
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0de367d3bc7e..5ee32d61e1c1 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -36,6 +36,7 @@ import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.media.IDevicesForAttributesCallback;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IPreferredMixerAttributesDispatcher;
@@ -356,6 +357,12 @@ interface IAudioService {
List<AudioDeviceAttributes> getDevicesForAttributesUnprotected(in AudioAttributes attributes);
+ void addOnDevicesForAttributesChangedListener(in AudioAttributes attributes,
+ in IDevicesForAttributesCallback callback);
+
+ oneway void removeOnDevicesForAttributesChangedListener(
+ in IDevicesForAttributesCallback callback);
+
int setAllowedCapturePolicy(in int capturePolicy);
int getAllowedCapturePolicy();
diff --git a/media/java/android/media/IDevicesForAttributesCallback.aidl b/media/java/android/media/IDevicesForAttributesCallback.aidl
new file mode 100644
index 000000000000..489ecf6081e2
--- /dev/null
+++ b/media/java/android/media/IDevicesForAttributesCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.media;
+
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
+
+/**
+ * AIDL for AudioService to signal updates of audio devices routing for attributes.
+ *
+ * {@hide}
+ */
+oneway interface IDevicesForAttributesCallback {
+
+ void onDevicesForAttributesChanged(in AudioAttributes attributes, boolean forVolume,
+ in List<AudioDeviceAttributes> devices);
+
+}
+
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 58078cf39227..aacea3d6b5c7 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -609,6 +609,7 @@ void FilterClientCallbackImpl::getSectionEvent(jobjectArray &arr, const int size
jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
@@ -639,6 +640,7 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
adGainCenter, adGainFront, adGainSurround);
+ env->DeleteLocalRef(adClazz);
}
jlong dataLength = mediaEvent.dataLength;
@@ -685,6 +687,7 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
env->DeleteLocalRef(audioDescriptor);
}
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,
@@ -701,6 +704,7 @@ void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,
jobject obj = env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getTsRecordEvent(jobjectArray &arr, const int size,
@@ -741,6 +745,7 @@ void FilterClientCallbackImpl::getTsRecordEvent(jobjectArray &arr, const int siz
env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, pts, firstMbInSlice);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getMmtpRecordEvent(jobjectArray &arr, const int size,
@@ -762,6 +767,7 @@ void FilterClientCallbackImpl::getMmtpRecordEvent(jobjectArray &arr, const int s
mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int size,
@@ -782,6 +788,7 @@ void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int siz
itemFragmentIndex, lastItemFragmentIndex, dataLength);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getIpPayloadEvent(jobjectArray &arr, const int size,
@@ -795,6 +802,7 @@ void FilterClientCallbackImpl::getIpPayloadEvent(jobjectArray &arr, const int si
jobject obj = env->NewObject(eventClazz, eventInit, dataLength);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getTemiEvent(jobjectArray &arr, const int size,
@@ -815,6 +823,7 @@ void FilterClientCallbackImpl::getTemiEvent(jobjectArray &arr, const int size,
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(array);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getScramblingStatusEvent(jobjectArray &arr, const int size,
@@ -829,6 +838,7 @@ void FilterClientCallbackImpl::getScramblingStatusEvent(jobjectArray &arr, const
jobject obj = env->NewObject(eventClazz, eventInit, scramblingStatus);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getIpCidChangeEvent(jobjectArray &arr, const int size,
@@ -842,6 +852,7 @@ void FilterClientCallbackImpl::getIpCidChangeEvent(jobjectArray &arr, const int
jobject obj = env->NewObject(eventClazz, eventInit, cid);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::getRestartEvent(jobjectArray &arr, const int size,
@@ -854,6 +865,7 @@ void FilterClientCallbackImpl::getRestartEvent(jobjectArray &arr, const int size
jobject obj = env->NewObject(eventClazz, eventInit, startId);
env->SetObjectArrayElement(arr, size, obj);
env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::onFilterEvent(const vector<DemuxFilterEvent> &events) {
@@ -952,6 +964,7 @@ void FilterClientCallbackImpl::onFilterEvent(const vector<DemuxFilterEvent> &eve
"Filter object has been freed. Ignoring callback.");
}
env->DeleteLocalRef(array);
+ env->DeleteLocalRef(eventClazz);
}
void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
@@ -1058,6 +1071,7 @@ void FrontendClientCallbackImpl::onScanMessage(
executeOnScanMessage(env, clazz, frontend, type, message);
env->DeleteLocalRef(frontend);
}
+ env->DeleteLocalRef(clazz);
}
void FrontendClientCallbackImpl::executeOnScanMessage(
@@ -1184,6 +1198,7 @@ void FrontendClientCallbackImpl::executeOnScanMessage(
"Atsc3PlpInfo;)V"),
array);
env->DeleteLocalRef(array);
+ env->DeleteLocalRef(plpClazz);
break;
}
case FrontendScanMessageType::MODULATION: {
@@ -2195,6 +2210,7 @@ jobject JTuner::getFrontendStatus(jintArray types) {
static_cast<long>(s.get<FrontendStatus::Tag::innerFec>()));
env->SetObjectField(statusObj, field, newLongObj);
env->DeleteLocalRef(newLongObj);
+ env->DeleteLocalRef(longClazz);
break;
}
case FrontendStatus::Tag::modulationStatus: {
@@ -2360,6 +2376,7 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, valObj);
env->DeleteLocalRef(valObj);
+ env->DeleteLocalRef(plpClazz);
break;
}
case FrontendStatus::Tag::modulations: {
@@ -2770,6 +2787,7 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, valObj);
env->DeleteLocalRef(valObj);
+ env->DeleteLocalRef(plpClazz);
break;
}
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
index 244b367423a4..51fc7ed64660 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml
@@ -18,6 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget">
- <uses-sdk android:minSdkVersion="29" />
+ <uses-sdk android:minSdkVersion="21" />
</manifest>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
index c799b9962828..02f69f679a46 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml
@@ -29,6 +29,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?android:attr/actionBarTheme" />
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/support_action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/actionBarTheme"
+ android:visibility="gone" />
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
new file mode 100644
index 000000000000..dcc6e5a37246
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 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.settingslib.collapsingtoolbar;
+
+import android.app.ActionBar;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toolbar;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.settingslib.utils.BuildCompatUtils;
+import com.android.settingslib.widget.R;
+
+import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.google.android.material.color.DynamicColors;
+
+/**
+ * A base Activity that has a collapsing toolbar layout is used for the activities intending to
+ * enable the collapsing toolbar function.
+ */
+public class CollapsingToolbarAppCompatActivity extends AppCompatActivity {
+
+ private class DelegateCallback implements CollapsingToolbarDelegate.HostCallback {
+ @Nullable
+ @Override
+ public ActionBar setActionBar(Toolbar toolbar) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public androidx.appcompat.app.ActionBar setActionBar(
+ androidx.appcompat.widget.Toolbar toolbar) {
+ CollapsingToolbarAppCompatActivity.super.setSupportActionBar(toolbar);
+ return CollapsingToolbarAppCompatActivity.super.getSupportActionBar();
+ }
+
+ @Override
+ public void setOuterTitle(CharSequence title) {
+ CollapsingToolbarAppCompatActivity.super.setTitle(title);
+ }
+ }
+
+ private CollapsingToolbarDelegate mToolbardelegate;
+
+ private int mCustomizeLayoutResId = 0;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (BuildCompatUtils.isAtLeastS()) {
+ DynamicColors.applyToActivityIfAvailable(this);
+ }
+ setTheme(R.style.Theme_SubSettingsBase);
+
+ if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) {
+ super.setContentView(mCustomizeLayoutResId);
+ return;
+ }
+
+ View view = getToolbarDelegate().onCreateView(getLayoutInflater(), null, this);
+ super.setContentView(view);
+ }
+
+ @Override
+ public void setContentView(int layoutResID) {
+ final ViewGroup parent = (mToolbardelegate == null) ? findViewById(R.id.content_frame)
+ : mToolbardelegate.getContentFrameLayout();
+ if (parent != null) {
+ parent.removeAllViews();
+ }
+ LayoutInflater.from(this).inflate(layoutResID, parent);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ final ViewGroup parent = (mToolbardelegate == null) ? findViewById(R.id.content_frame)
+ : mToolbardelegate.getContentFrameLayout();
+ if (parent != null) {
+ parent.addView(view);
+ }
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ final ViewGroup parent = (mToolbardelegate == null) ? findViewById(R.id.content_frame)
+ : mToolbardelegate.getContentFrameLayout();
+ if (parent != null) {
+ parent.addView(view, params);
+ }
+ }
+
+ /**
+ * This method allows an activity to replace the default layout with a customize layout. Notice
+ * that it will no longer apply the features being provided by this class when this method
+ * gets called.
+ */
+ protected void setCustomizeContentView(int layoutResId) {
+ mCustomizeLayoutResId = layoutResId;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ getToolbarDelegate().setTitle(title);
+ }
+
+ @Override
+ public void setTitle(int titleId) {
+ setTitle(getText(titleId));
+ }
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
+ getSupportFragmentManager().popBackStackImmediate();
+ }
+
+ // Closes the activity if there is no fragment inside the stack. Otherwise the activity will
+ // has a blank screen since there is no any fragment.
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ finishAfterTransition();
+ }
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+
+ // Closes the activity if there is no fragment inside the stack. Otherwise the activity will
+ // has a blank screen since there is no any fragment. onBackPressed() in Activity.java only
+ // handles popBackStackImmediate(). This will close activity to avoid a blank screen.
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ finishAfterTransition();
+ }
+ }
+
+ /**
+ * Returns an instance of collapsing toolbar.
+ */
+ @Nullable
+ public CollapsingToolbarLayout getCollapsingToolbarLayout() {
+ return getToolbarDelegate().getCollapsingToolbarLayout();
+ }
+
+ /**
+ * Return an instance of app bar.
+ */
+ @Nullable
+ public AppBarLayout getAppBarLayout() {
+ return getToolbarDelegate().getAppBarLayout();
+ }
+
+ private CollapsingToolbarDelegate getToolbarDelegate() {
+ if (mToolbardelegate == null) {
+ mToolbardelegate = new CollapsingToolbarDelegate(new DelegateCallback());
+ }
+ return mToolbardelegate;
+ }
+}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 8c8b47875bec..01f92c4fa7b1 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -117,12 +117,30 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
@Override
public boolean onNavigateUp() {
- if (!super.onNavigateUp()) {
+ if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
+ getSupportFragmentManager().popBackStackImmediate();
+ }
+
+ // Closes the activity if there is no fragment inside the stack. Otherwise the activity will
+ // has a blank screen since there is no any fragment.
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finishAfterTransition();
}
return true;
}
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+
+ // Closes the activity if there is no fragment inside the stack. Otherwise the activity will
+ // has a blank screen since there is no any fragment. onBackPressed() in Activity.java only
+ // handles popBackStackImmediate(). This will close activity to avoid a blank screen.
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ finishAfterTransition();
+ }
+ }
+
/**
* Returns an instance of collapsing toolbar.
*/
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index 01698b7937aa..1c2288acd358 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -19,9 +19,11 @@ package com.android.settingslib.collapsingtoolbar;
import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
import android.app.ActionBar;
+import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.text.LineBreakConfig;
import android.os.Build;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,6 +32,7 @@ import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.android.settingslib.widget.R;
@@ -42,7 +45,7 @@ import com.google.android.material.appbar.CollapsingToolbarLayout;
* extend from {@link CollapsingToolbarBaseActivity} or from {@link CollapsingToolbarBaseFragment}.
*/
public class CollapsingToolbarDelegate {
-
+ private static final String TAG = "CTBdelegate";
/** Interface to be implemented by the host of the Collapsing Toolbar. */
public interface HostCallback {
/**
@@ -53,6 +56,13 @@ public class CollapsingToolbarDelegate {
@Nullable
ActionBar setActionBar(Toolbar toolbar);
+ /** Sets support tool bar and return support action bar, this is for AppCompatActivity. */
+ @Nullable
+ default androidx.appcompat.app.ActionBar setActionBar(
+ androidx.appcompat.widget.Toolbar toolbar) {
+ return null;
+ }
+
/** Sets a title on the host. */
void setOuterTitle(CharSequence title);
}
@@ -79,6 +89,13 @@ public class CollapsingToolbarDelegate {
/** Method to call that creates the root view of the collapsing toolbar. */
@SuppressWarnings("RestrictTo")
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
+ return onCreateView(inflater, container, null);
+ }
+
+ /** Method to call that creates the root view of the collapsing toolbar. */
+ @SuppressWarnings("RestrictTo")
+ View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ Activity activity) {
final View view =
inflater.inflate(R.layout.collapsing_toolbar_base_layout, container, false);
if (view instanceof CoordinatorLayout) {
@@ -99,17 +116,57 @@ public class CollapsingToolbarDelegate {
}
}
autoSetCollapsingToolbarLayoutScrolling();
- mToolbar = view.findViewById(R.id.action_bar);
mContentFrameLayout = view.findViewById(R.id.content_frame);
- final ActionBar actionBar = mHostCallback.setActionBar(mToolbar);
+ if (activity instanceof AppCompatActivity) {
+ Log.d(TAG, "onCreateView: from AppCompatActivity and sub-class.");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ initSupportActionBar(inflater);
+ } else {
+ initRSupportActionBar(view);
+ }
+ } else {
+ Log.d(TAG, "onCreateView: from NonAppCompatActivity.");
+ mToolbar = view.findViewById(R.id.action_bar);
+ final ActionBar actionBar = mHostCallback.setActionBar(mToolbar);
+ // Enable title and home button by default
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+ }
+ }
+ return view;
+ }
- // Enable title and home button by default
+ private void initSupportActionBar(@NonNull LayoutInflater inflater) {
+ if (mCollapsingToolbarLayout == null) {
+ return;
+ }
+ mCollapsingToolbarLayout.removeAllViews();
+ inflater.inflate(R.layout.support_toolbar, mCollapsingToolbarLayout);
+ final androidx.appcompat.widget.Toolbar supportToolbar =
+ mCollapsingToolbarLayout.findViewById(R.id.support_action_bar);
+ final androidx.appcompat.app.ActionBar actionBar =
+ mHostCallback.setActionBar(supportToolbar);
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+ }
+ }
+
+ private void initRSupportActionBar(View view) {
+ view.findViewById(R.id.action_bar).setVisibility(View.GONE);
+ final androidx.appcompat.widget.Toolbar supportToolbar =
+ view.findViewById(R.id.support_action_bar);
+ supportToolbar.setVisibility(View.VISIBLE);
+ final androidx.appcompat.app.ActionBar actionBar =
+ mHostCallback.setActionBar(supportToolbar);
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayShowTitleEnabled(true);
}
- return view;
}
/** Return an instance of CoordinatorLayout. */
@@ -160,9 +217,13 @@ public class CollapsingToolbarDelegate {
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
- // Header can be scrolling while device in landscape mode.
- return appBarLayout.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ // Header can be scrolling while device in landscape mode and SDK > 33
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
+ return false;
+ } else {
+ return appBarLayout.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
}
});
params.setBehavior(behavior);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index d67ac3b86050..e4e34f8cae1e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -49,7 +49,7 @@ import com.google.android.material.appbar.CollapsingToolbarLayout;
*/
@RequiresApi(Build.VERSION_CODES.S)
public class CollapsingCoordinatorLayout extends CoordinatorLayout {
- private static final String TAG = "CollapsingCoordinatorLayout";
+ private static final String TAG = "CollapsingCoordinator";
private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f;
private CharSequence mToolbarTitle;
@@ -255,9 +255,13 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout {
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
- // Header can be scrolling while device in landscape mode.
- return appBarLayout.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ // Header can be scrolling while device in landscape mode and SDK > 33
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
+ return false;
+ } else {
+ return appBarLayout.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
}
});
params.setBehavior(behavior);
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index 59ae1221ddd3..b39d09f38b62 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -18,7 +18,11 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="match_parent">
+ android:layout_width="match_parent"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@+id/switch_text"
@@ -50,7 +54,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginEnd="@dimen/settingslib_switchbar_subsettings_margin_end"
android:focusable="false"
android:clickable="false"
android:theme="@style/SwitchBar.Switch.Settingslib"/>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-sw600dp/dmiens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-sw600dp/dmiens.xml
deleted file mode 100644
index 55a2589102d3..000000000000
--- a/packages/SettingsLib/MainSwitchPreference/res/values-sw600dp/dmiens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 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.
- -->
-
-<resources>
-
- <!-- SwitchBar sub settings margin start / end -->
- <dimen name="settingslib_switchbar_subsettings_margin_start">80dp</dimen>
-</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-sw720dp-land/dmiens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-sw720dp-land/dmiens.xml
deleted file mode 100644
index 53995bcf055b..000000000000
--- a/packages/SettingsLib/MainSwitchPreference/res/values-sw720dp-land/dmiens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 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.
- -->
-
-<resources>
-
- <!-- SwitchBar sub settings margin start / end -->
- <dimen name="settingslib_switchbar_subsettings_margin_start">128dp</dimen>
- <dimen name="settingslib_switchbar_subsettings_margin_end">128dp</dimen>
-</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-sw720dp/dmiens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-sw720dp/dmiens.xml
deleted file mode 100644
index 9015c581eff5..000000000000
--- a/packages/SettingsLib/MainSwitchPreference/res/values-sw720dp/dmiens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 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.
- -->
-
-<resources>
-
- <!-- SwitchBar sub settings margin start / end -->
- <dimen name="settingslib_switchbar_subsettings_margin_start">80dp</dimen>
- <dimen name="settingslib_switchbar_subsettings_margin_end">80dp</dimen>
-</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 157a54e3573d..88b2c8728495 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -27,6 +27,6 @@
<dimen name="settingslib_switch_title_margin">24dp</dimen>
<!-- SwitchBar sub settings margin start / end -->
- <dimen name="settingslib_switchbar_subsettings_margin_start">72dp</dimen>
+ <dimen name="settingslib_switchbar_subsettings_margin_start">56dp</dimen>
<dimen name="settingslib_switchbar_subsettings_margin_end">16dp</dimen>
</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 86fec50d7c21..864a8bb17058 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -161,6 +161,19 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec
}
/**
+ * Set icon space reserved for title
+ */
+ public void setIconSpaceReserved(boolean iconSpaceReserved) {
+ if (mTextView != null && !BuildCompatUtils.isAtLeastS()) {
+ LayoutParams params = (LayoutParams) mTextView.getLayoutParams();
+ int iconSpace = getContext().getResources().getDimensionPixelSize(
+ R.dimen.settingslib_switchbar_subsettings_margin_start);
+ params.setMarginStart(iconSpaceReserved ? iconSpace : 0);
+ mTextView.setLayoutParams(params);
+ }
+ }
+
+ /**
* Show the MainSwitchBar
*/
public void show() {
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index fc0e05f7fb46..53cc268851e7 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -37,7 +37,6 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw
private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
private MainSwitchBar mMainSwitchBar;
- private CharSequence mTitle;
public MainSwitchPreference(Context context) {
super(context);
@@ -68,6 +67,10 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw
holder.setDividerAllowedBelow(false);
mMainSwitchBar = (MainSwitchBar) holder.findViewById(R.id.settingslib_main_switch_bar);
+ // To support onPreferenceChange callback, it needs to call callChangeListener() when
+ // MainSwitchBar is clicked.
+ mMainSwitchBar.setOnClickListener((view) -> callChangeListener(isChecked()));
+ setIconSpaceReserved(isIconSpaceReserved());
updateStatus(isChecked());
registerListenerToSwitchBar();
}
@@ -82,6 +85,10 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw
final CharSequence title = a.getText(
androidx.preference.R.styleable.Preference_android_title);
setTitle(title);
+
+ final boolean bIconSpaceReserved = a.getBoolean(
+ androidx.preference.R.styleable.Preference_android_iconSpaceReserved, true);
+ setIconSpaceReserved(bIconSpaceReserved);
a.recycle();
}
}
@@ -96,9 +103,17 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw
@Override
public void setTitle(CharSequence title) {
- mTitle = title;
+ super.setTitle(title);
if (mMainSwitchBar != null) {
- mMainSwitchBar.setTitle(mTitle);
+ mMainSwitchBar.setTitle(title);
+ }
+ }
+
+ @Override
+ public void setIconSpaceReserved(boolean iconSpaceReserved) {
+ super.setIconSpaceReserved(iconSpaceReserved);
+ if (mMainSwitchBar != null) {
+ mMainSwitchBar.setIconSpaceReserved(iconSpaceReserved);
}
}
@@ -113,7 +128,7 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw
public void updateStatus(boolean checked) {
setChecked(checked);
if (mMainSwitchBar != null) {
- mMainSwitchBar.setTitle(mTitle);
+ mMainSwitchBar.setTitle(getTitle());
mMainSwitchBar.show();
}
}
@@ -125,6 +140,7 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw
if (!mSwitchChangeListeners.contains(listener)) {
mSwitchChangeListeners.add(listener);
}
+
if (mMainSwitchBar != null) {
mMainSwitchBar.addOnSwitchChangeListener(listener);
}
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml
index bda478e6e4fe..f1e028b405db 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml
@@ -17,6 +17,7 @@
<resources>
<style name="PreferenceTheme.SettingsLib" parent="@style/PreferenceThemeOverlay">
<item name="preferenceCategoryTitleTextAppearance">@style/TextAppearance.CategoryTitle.SettingsLib</item>
+ <item name="preferenceScreenStyle">@style/SettingsPreferenceScreen.SettingsLib</item>
<item name="preferenceCategoryStyle">@style/SettingsCategoryPreference.SettingsLib</item>
<item name="preferenceStyle">@style/SettingsPreference.SettingsLib</item>
<item name="checkBoxPreferenceStyle">@style/SettingsCheckBoxPreference.SettingsLib</item>
@@ -28,6 +29,11 @@
<item name="footerPreferenceStyle">@style/Preference.Material</item>
</style>
+ <style name="SettingsPreferenceScreen.SettingsLib" parent="@style/Preference.PreferenceScreen.Material">
+ <item name="layout">@layout/settingslib_preference</item>
+ <item name="iconSpaceReserved">@bool/settingslib_config_icon_space_reserved</item>
+ </style>
+
<style name="SettingsCategoryPreference.SettingsLib" parent="@style/Preference.Category.Material">
<item name="iconSpaceReserved">@bool/settingslib_config_icon_space_reserved</item>
<item name="allowDividerAbove">@bool/settingslib_config_allow_divider</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index 328ab46ed2f9..af3fc4817c45 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -28,19 +28,19 @@
</style>
<style name="TextAppearance.TopIntroText"
- parent="@android:style/TextAppearance.DeviceDefault">
+ parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearance.EntityHeaderTitle"
- parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
+ parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">20sp</item>
</style>
<style name="TextAppearance.EntityHeaderSummary"
- parent="@android:style/TextAppearance.DeviceDefault">
+ parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:textAlignment">viewStart</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:singleLine">true</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 2845916fc2dd..8508878e95d7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1368,7 +1368,7 @@
<string name="user_add_user_message_long">You can share this device with other people by creating additional users. Each user has their own space, which they can customize with apps, wallpaper, and so on. Users can also adjust device settings like Wi\u2011Fi that affect everyone.\n\nWhen you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. Accessibility settings and services may not transfer to the new user.</string>
<!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] -->
<string name="user_add_user_message_short">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string>
- <!-- Title for grant user admin privileges dialog [CHAR LIMIT=30] -->
+ <!-- Title for grant user admin privileges dialog [CHAR LIMIT=65] -->
<string name="user_grant_admin_title">Give this user admin privileges?</string>
<!-- Message for grant admin privileges dialog. [CHAR LIMIT=none] -->
<string name="user_grant_admin_message">As an admin, they will be able to manage other users, modify device settings and factory reset the device.</string>
diff --git a/packages/SystemUI/docs/modern-architecture.png b/packages/SystemUI/docs/modern-architecture.png
new file mode 100644
index 000000000000..2636362dd2ec
--- /dev/null
+++ b/packages/SystemUI/docs/modern-architecture.png
Binary files differ
diff --git a/packages/SystemUI/docs/status-bar-data-pipeline.md b/packages/SystemUI/docs/status-bar-data-pipeline.md
new file mode 100644
index 000000000000..9fcdce1ef717
--- /dev/null
+++ b/packages/SystemUI/docs/status-bar-data-pipeline.md
@@ -0,0 +1,261 @@
+# Status Bar Data Pipeline
+
+## Background
+
+The status bar is the UI shown at the top of the user's screen that gives them
+information about the time, notifications, and system status like mobile
+conectivity and battery level. This document is about the implementation of the
+wifi and mobile system icons on the right side:
+
+![image of status bar](status-bar.png)
+
+In Android U, the data pipeline that determines what mobile and wifi icons to
+show in the status bar has been re-written with a new architecture. This format
+generally follows Android best practices to
+[app architecture](https://developer.android.com/topic/architecture#recommended-app-arch).
+This document serves as a guide for the new architecture, and as a guide for how
+OEMs can add customizations to the new architecture.
+
+## Architecture
+
+In the new architecture, there is a separate pipeline for each type of icon. For
+Android U, **only the wifi icon and mobile icons have been implemented in the
+new architecture**.
+
+As shown in the Android best practices guide, each new pipeline has a data
+layer, a domain layer, and a UI layer:
+
+![diagram of UI, domain, and data layers](modern-architecture.png)
+
+The classes in the data layer are `repository` instances. The classes in the
+domain layer are `interactor` instances. The classes in the UI layer are
+`viewmodel` instances and `viewbinder` instances. In this document, "repository"
+and "data layer" will be used interchangably (and the same goes for the other
+layers).
+
+The wifi logic is in `statusbar/pipeline/wifi` and the mobile logic is in
+`statusbar/pipeline/mobile`.
+
+#### Repository (data layer)
+
+System callbacks, broadcast receivers, configuration values are all defined
+here, and exposed through the appropriate interface. Where appropriate, we
+define `Model` objects at this layer so that clients do not have to rely on
+system-defined interfaces.
+
+#### Interactor (domain layer)
+
+Here is where we define the business logic and transform the data layer objects
+into something consumable by the ViewModel classes. For example,
+`MobileIconsInteractor` defines the CBRS filtering logic by exposing a
+`filteredSubscriptions` list.
+
+#### ViewModel (UI layer)
+
+View models should define the final piece of business logic mapping to UI logic.
+For example, the mobile view model checks the `IconInteractor.isRoaming` flow to
+decide whether or not to show the roaming indicator.
+
+#### ViewBinder
+
+These have already been implemented and configured. ViewBinders replace the old
+`applyMobileState` mechanism that existed in the `IconManager` classes of the
+old pipeline. A view binder associates a ViewModel with a View, and keeps the
+view up-to-date with the most recent information from the model.
+
+Any new fields added to the ViewModel classes need to be equivalently bound to
+the view here.
+
+### Putting it all together
+
+Putting that altogether, we have this overall architecture diagram for the
+icons:
+
+![diagram of wifi and mobile pipelines](status-bar-pipeline.png)
+
+### Mobile icons architecture
+
+Because there can be multiple mobile connections at the same time, the mobile
+pipeline is split up hierarchically. At each level (data, domain, and UI), there
+is a singleton parent class that manages information relevant to **all** mobile
+connections, and multiple instances of child classes that manage information for
+a **single** mobile connection.
+
+For example, `MobileConnectionsRepository` is a singleton at the data layer that
+stores information relevant to **all** mobile connections, and it also manages a
+list of child `MobileConnectionRepository` classes. `MobileConnectionRepository`
+is **not** a singleton, and each individual `MobileConnectionRepository`
+instance fully qualifies the state of a **single** connection. This pattern is
+repeated at the `Interactor` and `ViewModel` layers for mobile.
+
+![diagram of mobile parent child relationship](status-bar-mobile-pipeline.png)
+
+Note: Since there is at most one wifi connection, the wifi pipeline is not split
+up in the same way.
+
+## Customizations
+
+The new pipeline completely replaces these classes:
+
+* `WifiStatusTracker`
+* `MobileStatusTracker`
+* `NetworkSignalController` and `NetworkSignalControllerImpl`
+* `MobileSignalController`
+* `WifiSignalController`
+* `StatusBarSignalPolicy` (including `SignalIconState`, `MobileIconState`, and
+ `WifiIconState`)
+
+Any customizations in any of these classes will need to be migrated to the new
+pipeline. As a general rule, any change that would have gone into
+`NetworkControllerImpl` would be done in `MobileConnectionsRepository`, and any
+change for `MobileSignalController` can be done in `MobileConnectionRepository`
+(see above on the relationship between those repositories).
+
+### Sample customization: New service
+
+Some customizations require listening to additional services to get additional
+data. This new architecture makes it easy to add additional services to the
+status bar data pipeline to get icon customizations.
+
+Below is a general guide to how a new service should be added. However, there
+may be exceptions to this guide for specific use cases.
+
+1. In the data layer (`repository` classes), add a new `StateFlow` that listens
+ to the service:
+
+ ```kotlin
+ class MobileConnectionsRepositoryImpl {
+ ...
+ val fooVal: StateFlow<Int> =
+ conflatedCallbackFlow {
+ val callback = object : FooServiceCallback(), FooListener {
+ override fun onFooChanged(foo: Int) {
+ trySend(foo)
+ }
+ }
+
+ fooService.registerCallback(callback)
+
+ awaitClose { fooService.unregisterCallback(callback) }
+ }
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), FOO_DEFAULT_VAL)
+ }
+ ```
+
+1. In the domain layer (`interactor` classes), either use this new flow to
+ process values, or just expose the flow as-is for the UI layer.
+
+ For example, if `bar` should only be true when `foo` is positive:
+
+ ```kotlin
+ class MobileIconsInteractor {
+ ...
+ val bar: StateFlow<Boolean> =
+ mobileConnectionsRepo
+ .mapLatest { foo -> foo > 0 }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = false)
+ }
+ ```
+
+1. In the UI layer (`viewmodel` classes), update the existing flows to process
+ the new value from the interactor.
+
+ For example, if the icon should be hidden when `bar` is true:
+
+ ```kotlin
+ class MobileIconViewModel {
+ ...
+ iconId: Flow<Int> = combine(
+ iconInteractor.level,
+ iconInteractor.numberOfLevels,
+ iconInteractor.bar,
+ ) { level, numberOfLevels, bar ->
+ if (bar) {
+ null
+ } else {
+ calcIcon(level, numberOfLevels)
+ }
+ }
+ ```
+
+## Demo mode
+
+SystemUI demo mode is a first-class citizen in the new pipeline. It is
+implemented as an entirely separate repository,
+`DemoMobileConnectionsRepository`. When the system moves into demo mode, the
+implementation of the data layer is switched to the demo repository via the
+`MobileRepositorySwitcher` class.
+
+Because the demo mode repositories implement the same interfaces as the
+production classes, any changes made above will have to be implemented for demo
+mode as well.
+
+1. Following from above, if `fooVal` is added to the
+ `MobileConnectionsRepository` interface:
+
+ ```kotlin
+ class DemoMobileConnectionsRepository {
+ private val _fooVal = MutableStateFlow(FOO_DEFAULT_VALUE)
+ override val fooVal: StateFlow<Int> = _fooVal.asStateFlow()
+
+ // Process the state. **See below on how to add the command to the CLI**
+ fun processEnabledMobileState(state: Mobile) {
+ ...
+ _fooVal.value = state.fooVal
+ }
+ }
+ ```
+
+1. (Optional) If you want to enable the command line interface for setting and
+ testing this value in demo mode, you can add parsing logic to
+ `DemoModeMobileConnectionDataSource` and `FakeNetworkEventModel`:
+
+ ```kotlin
+ sealed interface FakeNetworkEventModel {
+ data class Mobile(
+ ...
+ // Add new fields here
+ val fooVal: Int?
+ )
+ }
+ ```
+
+ ```kotlin
+ class DemoModeMobileConnectionDataSource {
+ // Currently, the demo commands are implemented as an extension function on Bundle
+ private fun Bundle.activeMobileEvent(): Mobile {
+ ...
+ val fooVal = getString("fooVal")?.toInt()
+ return Mobile(
+ ...
+ fooVal = fooVal,
+ )
+ }
+ }
+ ```
+
+If step 2 is implemented, then you will be able to pass demo commands via the
+command line:
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e fooVal <test value>
+```
+
+## Migration plan
+
+For Android U, the new pipeline will be enabled and default. However, the old
+pipeline code will still be around just in case the new pipeline doesn’t do well
+in the testing phase.
+
+For Android V, the old pipeline will be completely removed and the new pipeline
+will be the one source of truth.
+
+Our ask for OEMs is to default to using the new pipeline in Android U. If there
+are customizations that seem difficult to migrate over to the new pipeline,
+please file a bug with us and we’d be more than happy to consult on the best
+solution. The new pipeline was designed with customizability in mind, so our
+hope is that working the new pipeline will be easier and faster.
+
+Note: The new pipeline currently only supports the wifi and mobile icons. The
+other system status bar icons may be migrated to a similar architecture in the
+future.
diff --git a/packages/SystemUI/docs/status-bar-mobile-pipeline.png b/packages/SystemUI/docs/status-bar-mobile-pipeline.png
new file mode 100644
index 000000000000..620563de3daa
--- /dev/null
+++ b/packages/SystemUI/docs/status-bar-mobile-pipeline.png
Binary files differ
diff --git a/packages/SystemUI/docs/status-bar-pipeline.png b/packages/SystemUI/docs/status-bar-pipeline.png
new file mode 100644
index 000000000000..1c568c9bcda9
--- /dev/null
+++ b/packages/SystemUI/docs/status-bar-pipeline.png
Binary files differ
diff --git a/packages/SystemUI/docs/status-bar.png b/packages/SystemUI/docs/status-bar.png
new file mode 100644
index 000000000000..3a5af0e2c3e0
--- /dev/null
+++ b/packages/SystemUI/docs/status-bar.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 76c1158373b7..96bfa4323f95 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -643,6 +643,7 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ @Nullable
static BluetoothAdapter provideBluetoothAdapter(BluetoothManager bluetoothManager) {
return bluetoothManager.getAdapter();
}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 3e111e6de785..302d6a9ca1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -38,7 +38,7 @@ class StylusManager
@Inject
constructor(
private val inputManager: InputManager,
- private val bluetoothAdapter: BluetoothAdapter,
+ private val bluetoothAdapter: BluetoothAdapter?,
@Background private val handler: Handler,
@Background private val executor: Executor,
) : InputManager.InputDeviceListener, BluetoothAdapter.OnMetadataChangedListener {
@@ -141,7 +141,7 @@ constructor(
}
private fun onStylusBluetoothConnected(btAddress: String) {
- val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(btAddress) ?: return
+ val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return
try {
bluetoothAdapter.addOnMetadataChangedListener(device, executor, this)
} catch (e: IllegalArgumentException) {
@@ -150,7 +150,7 @@ constructor(
}
private fun onStylusBluetoothDisconnected(btAddress: String) {
- val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(btAddress) ?: return
+ val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return
try {
bluetoothAdapter.removeOnMetadataChangedListener(device, this)
} catch (e: IllegalArgumentException) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b3f8af533814..81fbd78e2b62 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -4281,6 +4281,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mContexts == null) {
mContexts = new ArrayList<>(1);
}
+ mContexts.add(new FillContext(requestId, new AssistStructure(), mCurrentViewId));
if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
inlineSuggestionsRequest = null;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0f7a72590376..78bff9524b10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -106,6 +106,7 @@ import android.media.IAudioService;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.media.IDevicesForAttributesCallback;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IPreferredMixerAttributesDispatcher;
@@ -3124,6 +3125,25 @@ public class AudioService extends IAudioService.Stub
return mAudioSystem.getDevicesForAttributes(attributes, forVolume);
}
+ /**
+ * @see AudioManager#addOnDevicesForAttributesChangedListener(
+ * AudioAttributes, Executor, OnDevicesForAttributesChangedListener)
+ */
+ public void addOnDevicesForAttributesChangedListener(AudioAttributes attributes,
+ IDevicesForAttributesCallback callback) {
+ mAudioSystem.addOnDevicesForAttributesChangedListener(
+ attributes, false /* forVolume */, callback);
+ }
+
+ /**
+ * @see AudioManager#removeOnDevicesForAttributesChangedListener(
+ * OnDevicesForAttributesChangedListener)
+ */
+ public void removeOnDevicesForAttributesChangedListener(
+ IDevicesForAttributesCallback callback) {
+ mAudioSystem.removeOnDevicesForAttributesChangedListener(callback);
+ }
+
// pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,
// KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE
public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv,
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 7fefc556a02f..7839ada9d5b2 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -22,10 +22,15 @@ import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioMixerAttributes;
import android.media.AudioSystem;
+import android.media.IDevicesForAttributesCallback;
import android.media.ISoundDose;
import android.media.ISoundDoseCallback;
import android.media.audiopolicy.AudioMix;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -64,8 +69,21 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
private static final boolean USE_CACHE_FOR_GETDEVICES = true;
private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
+ mLastDevicesForAttr = new ConcurrentHashMap<>();
+ private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
+ private final Object mDeviceCacheLock = new Object();
private int[] mMethodCacheHit;
+ /**
+ * Map that stores all attributes + forVolume pairs that are registered for
+ * routing change callback. The key is the {@link IBinder} that corresponds
+ * to the remote callback.
+ */
+ private final ArrayMap<IBinder, List<Pair<AudioAttributes, Boolean>>> mRegisteredAttributesMap =
+ new ArrayMap<>();
+ private final RemoteCallbackList<IDevicesForAttributesCallback>
+ mDevicesForAttributesCallbacks = new RemoteCallbackList<>();
+
private static final Object sRoutingListenerLock = new Object();
@GuardedBy("sRoutingListenerLock")
private static @Nullable OnRoutingUpdatedListener sRoutingListener;
@@ -96,6 +114,34 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
if (listener != null) {
listener.onRoutingUpdatedFromNative();
}
+
+ synchronized (mRegisteredAttributesMap) {
+ final int nbCallbacks = mDevicesForAttributesCallbacks.beginBroadcast();
+
+ for (int i = 0; i < nbCallbacks; i++) {
+ IDevicesForAttributesCallback cb =
+ mDevicesForAttributesCallbacks.getBroadcastItem(i);
+ List<Pair<AudioAttributes, Boolean>> attrList =
+ mRegisteredAttributesMap.get(cb.asBinder());
+
+ if (attrList == null) {
+ throw new IllegalStateException("Attribute list must not be null");
+ }
+
+ for (Pair<AudioAttributes, Boolean> attr : attrList) {
+ ArrayList<AudioDeviceAttributes> devices =
+ getDevicesForAttributes(attr.first, attr.second);
+ if (!mLastDevicesForAttr.containsKey(attr)
+ || !sameDeviceList(devices, mLastDevicesForAttr.get(attr))) {
+ try {
+ cb.onDevicesForAttributesChanged(
+ attr.first, attr.second, devices);
+ } catch (RemoteException e) { }
+ }
+ }
+ }
+ mDevicesForAttributesCallbacks.finishBroadcast();
+ }
}
interface OnRoutingUpdatedListener {
@@ -109,6 +155,66 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
}
/**
+ * @see AudioManager#addOnDevicesForAttributesChangedListener(
+ * AudioAttributes, Executor, OnDevicesForAttributesChangedListener)
+ */
+ public void addOnDevicesForAttributesChangedListener(AudioAttributes attributes,
+ boolean forVolume, @NonNull IDevicesForAttributesCallback listener) {
+ List<Pair<AudioAttributes, Boolean>> res;
+ final Pair<AudioAttributes, Boolean> attr = new Pair(attributes, forVolume);
+ synchronized (mRegisteredAttributesMap) {
+ res = mRegisteredAttributesMap.get(listener.asBinder());
+ if (res == null) {
+ res = new ArrayList<>();
+ mRegisteredAttributesMap.put(listener.asBinder(), res);
+ mDevicesForAttributesCallbacks.register(listener);
+ }
+
+ if (!res.contains(attr)) {
+ res.add(attr);
+ }
+ }
+
+ // Make query on registration to populate cache
+ getDevicesForAttributes(attributes, forVolume);
+ }
+
+ /**
+ * @see AudioManager#removeOnDevicesForAttributesChangedListener(
+ * OnDevicesForAttributesChangedListener)
+ */
+ public void removeOnDevicesForAttributesChangedListener(
+ @NonNull IDevicesForAttributesCallback listener) {
+ synchronized (mRegisteredAttributesMap) {
+ if (!mRegisteredAttributesMap.containsKey(listener.asBinder())) {
+ Log.w(TAG, "listener to be removed is not found.");
+ return;
+ }
+ mRegisteredAttributesMap.remove(listener.asBinder());
+ mDevicesForAttributesCallbacks.unregister(listener);
+ }
+ }
+
+ /**
+ * Helper function to compare lists of {@link AudioDeviceAttributes}
+ * @return true if the two lists contains the same devices, false otherwise.
+ */
+ private boolean sameDeviceList(@NonNull List<AudioDeviceAttributes> a,
+ @NonNull List<AudioDeviceAttributes> b) {
+ for (AudioDeviceAttributes device : a) {
+ if (!b.contains(device)) {
+ return false;
+ }
+ }
+ for (AudioDeviceAttributes device : b) {
+ if (!a.contains(device)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Implementation of AudioSystem.VolumeRangeInitRequestCallback
*/
@Override
@@ -159,8 +265,11 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
if (DEBUG_CACHE) {
Log.d(TAG, "---- clearing cache ----------");
}
- if (mDevicesForAttrCache != null) {
- synchronized (mDevicesForAttrCache) {
+ synchronized (mDeviceCacheLock) {
+ if (mDevicesForAttrCache != null) {
+ // Save latest cache to determine routing updates
+ mLastDevicesForAttr.putAll(mDevicesForAttrCache);
+
mDevicesForAttrCache.clear();
}
}
@@ -189,7 +298,7 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
if (USE_CACHE_FOR_GETDEVICES) {
ArrayList<AudioDeviceAttributes> res;
final Pair<AudioAttributes, Boolean> key = new Pair(attributes, forVolume);
- synchronized (mDevicesForAttrCache) {
+ synchronized (mDeviceCacheLock) {
res = mDevicesForAttrCache.get(key);
if (res == null) {
res = AudioSystem.getDevicesForAttributes(attributes, forVolume);
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index b813995f1c08..3ae699b1dbba 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -684,6 +684,16 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
*/
@GuardedBy("mLock")
protected List<S> updateCachedServiceListLocked(@UserIdInt int userId, boolean disabled) {
+ if (mServiceNameResolver != null
+ && mServiceNameResolver.isConfiguredInMultipleMode()) {
+ // In multiple mode, we have multiple instances of AbstractPerUserSystemService, per
+ // user where each instance holds information needed to connect to a backend. An
+ // update operation in this mode needs to account for addition, deletion, change
+ // of backends and cannot be executed in the scope of a given
+ // AbstractPerUserSystemService.
+ return updateCachedServiceListMultiModeLocked(userId, disabled);
+ }
+ // isConfiguredInMultipleMode is false
final List<S> services = getServiceListForUserLocked(userId);
if (services == null) {
return null;
@@ -704,6 +714,19 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
return services;
}
+ @GuardedBy("mLock")
+ private List<S> updateCachedServiceListMultiModeLocked(int userId, boolean disabled) {
+ final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, false, null,
+ null);
+ List<S> services = new ArrayList<>();
+ synchronized (mLock) {
+ removeCachedServiceListLocked(resolvedUserId);
+ services = getServiceListForUserLocked(userId);
+ }
+ return services;
+ }
+
/**
* Gets the Settings property that defines the name of the component name used to bind this
* service to an external service, or {@code null} when the service is not defined by such
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index ddb19f0ebfb8..af025c0ef4da 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -154,7 +154,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
if (mMaster.mServiceNameResolver != null
&& mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
- updateServiceInfoListLocked();
+ // Update of multi configured mode should always happen in AbstractMasterSystemService
+ // as this class is not aware of the complete list of multiple backends. Since we
+ // should never end up in this state, it is safe to not do anything if we end up here
+ // through a different code path.
+ if (mMaster.debug) {
+ Slog.d(mTag, "Should not end up in updateLocked when "
+ + "isConfiguredInMultipleMode is true");
+ }
} else {
updateServiceInfoLocked();
}
diff --git a/services/core/java/com/android/server/power/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
index 1a9eae6b6d8d..32f1bcfaad76 100644
--- a/services/core/java/com/android/server/power/ShutdownCheckPoints.java
+++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
@@ -54,6 +54,7 @@ public final class ShutdownCheckPoints {
private static final int MAX_DUMP_FILES = 20;
private static final SimpleDateFormat DATE_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
+ private static final File[] EMPTY_FILE_ARRAY = {};
private final ArrayList<CheckPoint> mCheckPoints;
private final Injector mInjector;
@@ -381,6 +382,9 @@ public final class ShutdownCheckPoints {
return true;
}
});
+ if (files == null) {
+ return EMPTY_FILE_ARRAY;
+ }
Arrays.sort(files);
return files;
}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 1c44da1fdb89..5560b41b164f 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -6,6 +6,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
@@ -208,6 +209,34 @@ public class JobStoreTest {
assertEquals("Incorrect # of persisted tasks.", 0, jobStatusSet.size());
}
+ /**
+ * Test that dynamic constraints aren't written to disk.
+ */
+ @Test
+ public void testDynamicConstraintsNotPersisted() throws Exception {
+ JobInfo.Builder b = new Builder(42, mComponent).setPersisted(true);
+ JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
+ js.addDynamicConstraints(JobStatus.CONSTRAINT_BATTERY_NOT_LOW
+ | JobStatus.CONSTRAINT_CHARGING
+ | JobStatus.CONSTRAINT_IDLE
+ | JobStatus.CONSTRAINT_STORAGE_NOT_LOW);
+ assertTrue(js.hasBatteryNotLowConstraint());
+ assertTrue(js.hasChargingConstraint());
+ assertTrue(js.hasIdleConstraint());
+ assertTrue(js.hasStorageNotLowConstraint());
+ mTaskStoreUnderTest.add(js);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ assertEquals("Job count is incorrect.", 1, jobStatusSet.size());
+ JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertFalse(loaded.hasBatteryNotLowConstraint());
+ assertFalse(loaded.hasChargingConstraint());
+ assertFalse(loaded.hasIdleConstraint());
+ assertFalse(loaded.hasStorageNotLowConstraint());
+ }
+
@Test
public void testExtractUidFromJobFileName() {
File file = new File(mTestContext.getFilesDir(), "randomName");
diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
index 3268df2f83e6..8c3838b939ec 100644
--- a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
@@ -496,8 +496,10 @@ public class PendingJobQueueTest {
Random random = new Random(1); // Always use the same series of pseudo random values.
for (int i = 0; i < 5000; ++i) {
+ final boolean ui = random.nextBoolean();
+ final boolean ej = !ui && random.nextBoolean();
JobStatus job = createJobStatus("testPendingJobSorting_Random",
- createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(250));
+ createJobInfo(i).setExpedited(ej).setUserInitiated(ui), random.nextInt(250));
job.enqueueTime = random.nextInt(1_000_000);
jobQueue.add(job);
}
@@ -531,8 +533,10 @@ public class PendingJobQueueTest {
jobQueue.clear();
for (int i = 0; i < 300; ++i) {
+ final boolean ui = random.nextBoolean();
+ final boolean ej = !ui && random.nextBoolean();
JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
- createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(50));
+ createJobInfo(i).setExpedited(ej).setUserInitiated(ui), random.nextInt(50));
job.enqueueTime = random.nextInt(1_000_000);
job.overrideState = random.nextInt(4);
jobQueue.add(job);
@@ -553,8 +557,10 @@ public class PendingJobQueueTest {
jobQueue.clear();
for (int i = 0; i < 300; ++i) {
+ final boolean ui = random.nextFloat() < .02;
+ final boolean ej = !ui && random.nextFloat() < .03;
JobStatus job = createJobStatus("testPendingJobSortingTransitivity_Concentrated",
- createJobInfo(i).setExpedited(random.nextFloat() < .03),
+ createJobInfo(i).setExpedited(ej).setUserInitiated(ui),
random.nextInt(20));
job.enqueueTime = random.nextInt(250);
job.overrideState = random.nextFloat() < .01
@@ -653,10 +659,11 @@ public class PendingJobQueueTest {
}
private void checkPendingJobInvariants(PendingJobQueue jobQueue) {
+ final SparseBooleanArray eJobSeen = new SparseBooleanArray();
final SparseBooleanArray regJobSeen = new SparseBooleanArray();
// Latest priority enqueue times seen for each priority+namespace for each app.
final SparseArrayMap<String, SparseLongArray> latestPriorityRegEnqueueTimesPerUid =
- new SparseArrayMap();
+ new SparseArrayMap<>();
final SparseArrayMap<String, SparseLongArray> latestPriorityEjEnqueueTimesPerUid =
new SparseArrayMap<>();
final int noEntry = -1;
@@ -672,7 +679,8 @@ public class PendingJobQueueTest {
// Invariant #1: All jobs are sorted by override state
// Invariant #2: All jobs (for a UID) are sorted by priority order
// Invariant #3: Jobs (for a UID) with the same priority are sorted by enqueue time.
- // Invariant #4: EJs (for a UID) should be before regular jobs
+ // Invariant #4: User-initiated jobs (for a UID) should be before all other jobs.
+ // Invariant #5: EJs (for a UID) should be before regular jobs
// Invariant 1
if (prevOverrideState != job.overrideState) {
@@ -683,6 +691,7 @@ public class PendingJobQueueTest {
// to avoid confusion in the other checks.
latestPriorityEjEnqueueTimesPerUid.clear();
latestPriorityRegEnqueueTimesPerUid.clear();
+ eJobSeen.clear();
regJobSeen.clear();
prevOverrideState = job.overrideState;
}
@@ -718,10 +727,24 @@ public class PendingJobQueueTest {
}
latestPriorityEnqueueTimes.put(priority, job.enqueueTime);
- // Invariant 4
- if (!job.isRequestedExpeditedJob()) {
+ if (job.isRequestedExpeditedJob()) {
+ eJobSeen.put(uid, true);
+ } else if (!job.getJob().isUserInitiated()) {
regJobSeen.put(uid, true);
- } else if (regJobSeen.get(uid)) {
+ }
+
+ // Invariant 4
+ if (job.getJob().isUserInitiated()) {
+ if (eJobSeen.get(uid)) {
+ fail("UID " + uid + " had a UIJ ordered after an EJ");
+ }
+ if (regJobSeen.get(uid)) {
+ fail("UID " + uid + " had a UIJ ordered after a regular job");
+ }
+ }
+
+ // Invariant 5
+ if (job.isRequestedExpeditedJob() && regJobSeen.get(uid)) {
fail("UID " + uid + " had an EJ ordered after a regular job");
}
}
@@ -733,6 +756,9 @@ public class PendingJobQueueTest {
+ "/o" + job.overrideState
+ "/p" + job.getEffectivePriority()
+ "/b" + job.lastEvaluatedBias
- + "/" + job.isRequestedExpeditedJob() + "@" + job.enqueueTime;
+ + "/"
+ + (job.isRequestedExpeditedJob()
+ ? "e" : (job.getJob().isUserInitiated() ? "u" : "r"))
+ + "@" + job.enqueueTime;
}
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index 2e93c878ceac..3a520bcdc526 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -37,7 +37,7 @@ import com.google.android.material.slider.RangeSlider.OnChangeListener;
public class TouchLatencyActivity extends AppCompatActivity {
private static final int REFRESH_RATE_SLIDER_MIN = 20;
- private static final int REFRESH_RATE_SLIDER_STEP = 5;
+ private static final int REFRESH_RATE_SLIDER_STEP = 1;
private Menu mMenu;
private Mode[] mDisplayModes;