Merge "Fix race condition between PromptIconViewBinder and BiometricSizeBinder" into main
diff --git a/apct-tests/perftests/healthconnect/Android.bp b/apct-tests/perftests/healthconnect/Android.bp
new file mode 100644
index 0000000..c2d0a6f
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "HealthConnectPerfTests",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "apct-perftests-utils",
+ "collector-device-lib-platform",
+ ],
+
+ libs: ["android.test.base"],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
+ certificate: "platform",
+}
diff --git a/apct-tests/perftests/healthconnect/AndroidManifest.xml b/apct-tests/perftests/healthconnect/AndroidManifest.xml
new file mode 100644
index 0000000..6a6370b
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.healthconnect">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.healthconnect"/>
+</manifest>
diff --git a/apct-tests/perftests/healthconnect/AndroidTest.xml b/apct-tests/perftests/healthconnect/AndroidTest.xml
new file mode 100644
index 0000000..5036202
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/AndroidTest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs HealthConnectPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="HealthConnectPerfTests.apk" />
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.healthconnect" />
+ <option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+ </test>
+</configuration>
diff --git a/apct-tests/perftests/healthconnect/OWNERS b/apct-tests/perftests/healthconnect/OWNERS
new file mode 100644
index 0000000..da0b46a
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1219472
+
+arkivanov@google.com
+jstembridge@google.com
+pratyushmore@google.com
+itsleo@google.com
diff --git a/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt b/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt
new file mode 100644
index 0000000..21a4ca0
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.perftests.healthconnect
+
+import android.health.connect.HealthConnectManager
+import android.os.SystemClock
+import android.perftests.utils.PerfStatusReporter
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Read/write benchmark tests for [HealthConnectManager]
+ *
+ * Build/Install/Run: atest HealthConnectReadWritePerfTest
+ */
+@RunWith(AndroidJUnit4::class)
+class HealthConnectReadWritePerfTest {
+
+ @get:Rule
+ val perfStatusReporter = PerfStatusReporter()
+
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+
+ private val manager by lazy {
+ requireNotNull(context.getSystemService(HealthConnectManager::class.java))
+ }
+
+ /**
+ * A first empty test just to setup the test package and make sure it runs properly.
+ */
+ @Test
+ fun placeholder() {
+ val state = perfStatusReporter.benchmarkState
+ while (state.keepRunning()) {
+ SystemClock.sleep(100)
+ }
+ }
+}
\ No newline at end of file
diff --git a/api/Android.bp b/api/Android.bp
index 9029d25..363197a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -307,10 +307,6 @@
"framework-protos",
],
flags: [
- "--api-lint-ignore-prefix android.icu.",
- "--api-lint-ignore-prefix java.",
- "--api-lint-ignore-prefix junit.",
- "--api-lint-ignore-prefix org.",
"--error NoSettingsProvider",
"--error UnhiddenSystemApi",
"--error UnflaggedApi",
diff --git a/core/api/current.txt b/core/api/current.txt
index 008521a..b043503 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9315,10 +9315,14 @@
public final class StorageStats implements android.os.Parcelable {
method public int describeContents();
method public long getAppBytes();
+ method @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public long getAppBytesByDataType(int);
method public long getCacheBytes();
method public long getDataBytes();
method public long getExternalCacheBytes();
method public void writeToParcel(android.os.Parcel, int);
+ field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_FILE_TYPE_APK = 0; // 0x0
+ field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_FILE_TYPE_DM = 1; // 0x1
+ field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_LIB = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
}
@@ -23972,6 +23976,7 @@
method @Nullable public android.net.Uri getIconUri();
method @NonNull public String getId();
method @NonNull public CharSequence getName();
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
method public int getType();
method public int getVolume();
method public int getVolumeHandling();
@@ -23989,6 +23994,9 @@
field public static final String FEATURE_REMOTE_VIDEO_PLAYBACK = "android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2; // 0x2
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0; // 0x0
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1; // 0x1
field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
@@ -24029,6 +24037,7 @@
method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic();
method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityRestricted(@NonNull java.util.Set<java.lang.String>);
@@ -24242,6 +24251,7 @@
method public void release();
method public void selectRoute(@NonNull android.media.MediaRoute2Info);
method public void setVolume(int);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public boolean wasTransferRequestedBySelf();
}
public abstract static class MediaRouter2.TransferCallback {
@@ -24639,12 +24649,16 @@
method @Nullable public CharSequence getName();
method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getTransferReason();
method @NonNull public java.util.List<java.lang.String> getTransferableRoutes();
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.RoutingSessionInfo> CREATOR;
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_APP = 2; // 0x2
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_FALLBACK = 0; // 0x0
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1; // 0x1
}
public static final class RoutingSessionInfo.Builder {
@@ -24665,6 +24679,8 @@
method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
method @NonNull public android.media.RoutingSessionInfo.Builder setName(@Nullable CharSequence);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferInitiator(@Nullable android.os.UserHandle, @Nullable String);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferReason(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeMax(int);
@@ -43277,6 +43293,7 @@
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
@@ -45272,6 +45289,7 @@
method @Nullable public String getMncString();
method @Deprecated public String getNumber();
method public int getPortIndex();
+ method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
method public int getSimSlotIndex();
method public int getSubscriptionId();
method public int getSubscriptionType();
@@ -45353,6 +45371,9 @@
field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
@@ -45601,6 +45622,8 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled();
+ method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceSmsCapable();
+ method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceVoiceCapable();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
@@ -45610,9 +45633,9 @@
method @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) public boolean isPremiumCapabilityAvailableForPurchase(int);
method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method public boolean isRttSupported();
- method public boolean isSmsCapable();
+ method @Deprecated public boolean isSmsCapable();
method @Deprecated public boolean isTtyModeSupported();
- method public boolean isVoiceCapable();
+ method @Deprecated public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
@@ -53525,6 +53548,7 @@
method public android.transition.Transition getExitTransition();
method protected final int getFeatures();
method protected final int getForcedWindowFlags();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled();
method @Nullable public android.view.WindowInsetsController getInsetsController();
method @NonNull public abstract android.view.LayoutInflater getLayoutInflater();
method protected final int getLocalFeatures();
@@ -53602,6 +53626,7 @@
method public abstract void setFeatureInt(int, int);
method public void setFlags(int, int);
method public void setFormat(int);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean);
method public void setGravity(int);
method @RequiresPermission(android.Manifest.permission.HIDE_OVERLAY_WINDOWS) public final void setHideOverlayWindows(boolean);
method public void setIcon(@DrawableRes int);
@@ -53946,6 +53971,7 @@
method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public float getDesiredHdrHeadroom();
method public int getFitInsetsSides();
method public int getFitInsetsTypes();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled();
method public final CharSequence getTitle();
method public boolean isFitInsetsIgnoringVisibility();
method public boolean isHdrConversionEnabled();
@@ -53957,6 +53983,7 @@
method public void setFitInsetsIgnoringVisibility(boolean);
method public void setFitInsetsSides(int);
method public void setFitInsetsTypes(int);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean);
method public void setHdrConversionEnabled(boolean);
method public final void setTitle(CharSequence);
method public void setWallpaperTouchEventsEnabled(boolean);
diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java
index 8d25d7b..87d97d5 100644
--- a/core/java/android/app/usage/StorageStats.java
+++ b/core/java/android/app/usage/StorageStats.java
@@ -17,11 +17,16 @@
package android.app.usage;
import android.annotation.BytesLong;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Storage statistics for a UID, package, or {@link UserHandle} on a single
* storage volume.
@@ -29,10 +34,47 @@
* @see StorageStatsManager
*/
public final class StorageStats implements Parcelable {
- /** {@hide} */ public long codeBytes;
- /** {@hide} */ public long dataBytes;
- /** {@hide} */ public long cacheBytes;
- /** {@hide} */ public long externalCacheBytes;
+ /** @hide */ public long codeBytes;
+ /** @hide */ public long dataBytes;
+ /** @hide */ public long cacheBytes;
+ /** @hide */ public long apkBytes;
+ /** @hide */ public long libBytes;
+ /** @hide */ public long dmBytes;
+ /** @hide */ public long externalCacheBytes;
+
+ /** Represents all .apk files in application code path.
+ * Can be used as an input to {@link #getAppBytesByDataType(int)}
+ * to get the sum of sizes for files of this type.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public static final int APP_DATA_TYPE_FILE_TYPE_APK = 0;
+
+ /** Represents all .dm files in application code path.
+ * Can be used as an input to {@link #getAppBytesByDataType(int)}
+ * to get the sum of sizes for files of this type.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public static final int APP_DATA_TYPE_FILE_TYPE_DM = 1;
+
+ /** Represents lib/ in application code path.
+ * Can be used as an input to {@link #getAppBytesByDataType(int)}
+ * to get the size of lib/ directory.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public static final int APP_DATA_TYPE_LIB = 2;
+
+ /**
+ * Keep in sync with the file types defined above.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ @IntDef(flag = false, value = {
+ APP_DATA_TYPE_FILE_TYPE_APK,
+ APP_DATA_TYPE_FILE_TYPE_DM,
+ APP_DATA_TYPE_LIB,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppDataType {}
/**
* Return the size of app. This includes {@code APK} files, optimized
@@ -48,6 +90,27 @@
}
/**
+ * Return the size of the specified data type. This includes files stored under
+ * application code path.
+ * <p>
+ * If there is more than one package inside a uid, the return represents the aggregated
+ * stats when query StorageStat for package or uid.
+ * The data is not collected and the return defaults to 0 when query StorageStats for user.
+ *
+ * <p>
+ * Data is isolated for each user on a multiuser device.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public long getAppBytesByDataType(@AppDataType int dataType) {
+ switch (dataType) {
+ case APP_DATA_TYPE_FILE_TYPE_APK: return apkBytes;
+ case APP_DATA_TYPE_LIB: return libBytes;
+ case APP_DATA_TYPE_FILE_TYPE_DM: return dmBytes;
+ default: return 0;
+ }
+ }
+
+ /**
* Return the size of all data. This includes files stored under
* {@link Context#getDataDir()}, {@link Context#getCacheDir()},
* {@link Context#getCodeCacheDir()}.
@@ -98,6 +161,9 @@
this.codeBytes = in.readLong();
this.dataBytes = in.readLong();
this.cacheBytes = in.readLong();
+ this.apkBytes = in.readLong();
+ this.libBytes = in.readLong();
+ this.dmBytes = in.readLong();
this.externalCacheBytes = in.readLong();
}
@@ -111,6 +177,9 @@
dest.writeLong(codeBytes);
dest.writeLong(dataBytes);
dest.writeLong(cacheBytes);
+ dest.writeLong(apkBytes);
+ dest.writeLong(libBytes);
+ dest.writeLong(dmBytes);
dest.writeLong(externalCacheBytes);
}
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index a611255..4d9d911 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -35,3 +35,10 @@
description: " Feature flag to support filter based event query API"
bug: "194321117"
}
+
+flag {
+ name: "get_app_bytes_by_data_type_api"
+ namespace: "system_performance"
+ description: "Feature flag for collecting app data size by file type API"
+ bug: "294088945"
+}
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index 81cfc07..b919c4b 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -55,6 +55,18 @@
/** Size of cache used by the application. (e.g., /data/data/<app>/cache) */
public long cacheSize;
+ /** Size of .apk files of the application. */
+ /** @hide */
+ public long apkSize;
+
+ /** Size of the libraries of the application. */
+ /** @hide */
+ public long libSize;
+
+ /** Size of the .dm files of the application. */
+ /** @hide */
+ public long dmSize;
+
/**
* Size of the secure container on external storage holding the
* application's code.
@@ -108,6 +120,18 @@
sb.append(" cache=");
sb.append(cacheSize);
}
+ if (apkSize != 0) {
+ sb.append(" apk=");
+ sb.append(apkSize);
+ }
+ if (libSize != 0) {
+ sb.append(" lib=");
+ sb.append(libSize);
+ }
+ if (dmSize != 0) {
+ sb.append(" dm=");
+ sb.append(dmSize);
+ }
if (externalCodeSize != 0) {
sb.append(" extCode=");
sb.append(externalCodeSize);
@@ -149,6 +173,9 @@
codeSize = source.readLong();
dataSize = source.readLong();
cacheSize = source.readLong();
+ apkSize = source.readLong();
+ libSize = source.readLong();
+ dmSize = source.readLong();
externalCodeSize = source.readLong();
externalDataSize = source.readLong();
externalCacheSize = source.readLong();
@@ -162,6 +189,9 @@
codeSize = pStats.codeSize;
dataSize = pStats.dataSize;
cacheSize = pStats.cacheSize;
+ apkSize = pStats.apkSize;
+ libSize = pStats.libSize;
+ dmSize = pStats.dmSize;
externalCodeSize = pStats.externalCodeSize;
externalDataSize = pStats.externalDataSize;
externalCacheSize = pStats.externalCacheSize;
@@ -179,6 +209,9 @@
dest.writeLong(codeSize);
dest.writeLong(dataSize);
dest.writeLong(cacheSize);
+ dest.writeLong(apkSize);
+ dest.writeLong(libSize);
+ dest.writeLong(dmSize);
dest.writeLong(externalCodeSize);
dest.writeLong(externalDataSize);
dest.writeLong(externalCacheSize);
@@ -198,6 +231,9 @@
&& codeSize == otherStats.codeSize
&& dataSize == otherStats.dataSize
&& cacheSize == otherStats.cacheSize
+ && apkSize == otherStats.apkSize
+ && libSize == otherStats.libSize
+ && dmSize == otherStats.dmSize
&& externalCodeSize == otherStats.externalCodeSize
&& externalDataSize == otherStats.externalDataSize
&& externalCacheSize == otherStats.externalCacheSize
@@ -208,7 +244,8 @@
@Override
public int hashCode() {
return Objects.hash(packageName, userHandle, codeSize, dataSize,
- cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
+ apkSize, libSize, dmSize, cacheSize, externalCodeSize,
+ externalDataSize, externalCacheSize, externalMediaSize,
externalObbSize);
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 72c436e..e4af2da 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4940,6 +4940,13 @@
*/
public static final String COLUMN_IS_NTN = "is_ntn";
+ /**
+ * TelephonyProvider column name to indicate the service capability bitmasks.
+ *
+ * @hide
+ */
+ public static final String COLUMN_SERVICE_CAPABILITIES = "service_capabilities";
+
/** All columns in {@link SimInfo} table. */
private static final List<String> ALL_COLUMNS = List.of(
COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
@@ -5011,7 +5018,8 @@
COLUMN_USER_HANDLE,
COLUMN_SATELLITE_ENABLED,
COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
- COLUMN_IS_NTN
+ COLUMN_IS_NTN,
+ COLUMN_SERVICE_CAPABILITIES
);
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cbbe785..b957b31 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,7 +22,6 @@
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
@@ -38,7 +37,6 @@
import android.annotation.Size;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
@@ -53,13 +51,8 @@
import android.hardware.OverlayProperties;
import android.hardware.SyncFence;
import android.hardware.display.DeviceProductInfo;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
-import android.hardware.display.IDisplayManager;
-import android.hardware.display.IVirtualDisplayCallback;
-import android.hardware.display.VirtualDisplay;
import android.hardware.graphics.common.DisplayDecorationSupport;
import android.opengl.EGLDisplay;
import android.opengl.EGLSync;
@@ -68,8 +61,6 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -2355,92 +2346,6 @@
}
/**
- * Because this API is now going through {@link DisplayManager}, orientation and displayRect
- * will automatically be computed based on configuration changes. Because of this, the params
- * orientation and displayRect are ignored
- *
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code VirtualDisplay#resize(int, int, int)} instead.",
- trackingBug = 247078497)
- public static void setDisplayProjection(IBinder displayToken, int orientation,
- Rect layerStackRect, Rect displayRect) {
- DisplayManagerGlobal.getInstance().resizeVirtualDisplay(
- IVirtualDisplayCallback.Stub.asInterface(displayToken), layerStackRect.width(),
- layerStackRect.height(), 1);
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} with flag "
- + " {@code VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for mirroring instead.",
- trackingBug = 247078497)
- public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
- IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
- if (b == null) {
- throw new UnsupportedOperationException();
- }
-
- IDisplayManager dm = IDisplayManager.Stub.asInterface(b);
- try {
- dm.setDisplayIdToMirror(displayToken, layerStack);
- } catch (RemoteException e) {
- throw new UnsupportedOperationException(e);
- }
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code VirtualDisplay#setSurface(Surface)} instead.",
- trackingBug = 247078497)
- public static void setDisplaySurface(IBinder displayToken, Surface surface) {
- IVirtualDisplayCallback virtualDisplayCallback =
- IVirtualDisplayCallback.Stub.asInterface(displayToken);
- DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
- dm.setVirtualDisplaySurface(virtualDisplayCallback, surface);
- }
-
- /**
- * Secure is no longer supported because this is only called from outside system which cannot
- * create secure displays.
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} or "
- + "{@code DisplayManager#createVirtualDisplay()} instead.",
- trackingBug = 247078497)
- public static IBinder createDisplay(String name, boolean secure) {
- if (name == null) {
- throw new IllegalArgumentException("name must not be null");
- }
-
- // We don't have a size yet so pass in 1 for width and height since 0 is invalid
- VirtualDisplay vd = DisplayManager.createVirtualDisplay(name, 1 /* width */, 1 /* height */,
- INVALID_DISPLAY, null /* Surface */);
- return vd == null ? null : vd.getToken().asBinder();
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code VirtualDisplay#release()} instead.",
- trackingBug = 247078497)
- public static void destroyDisplay(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
-
- DisplayManagerGlobal.getInstance().releaseVirtualDisplay(
- IVirtualDisplayCallback.Stub.asInterface(displayToken));
- }
-
- /**
* Returns whether protected content is supported in GPU composition.
* @hide
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e4b709e..ec99459 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -32,6 +32,7 @@
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
@@ -2309,6 +2310,7 @@
protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
+ private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
static {
EMPTY_STATE_SET = StateSet.get(0);
@@ -2393,6 +2395,7 @@
| StateSet.VIEW_STATE_PRESSED);
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
+ sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
}
/**
@@ -33084,11 +33087,11 @@
private void votePreferredFrameRate() {
// use toolkitSetFrameRate flag to gate the change
- if (sToolkitSetFrameRateReadOnlyFlagValue) {
- ViewRootImpl viewRootImpl = getViewRootImpl();
- float sizePercentage = getSizePercentage();
- int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
- if (viewRootImpl != null && sizePercentage > 0) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ float sizePercentage = getSizePercentage();
+ int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
+ if (viewRootImpl != null && sizePercentage > 0) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
if (mPreferredFrameRate < 0) {
if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
@@ -33104,6 +33107,9 @@
}
viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
}
+ if (sToolkitMetricsForFrameRateDecisionFlagValue) {
+ viewRootImpl.recordViewPercentage(sizePercentage);
+ }
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9f6395e..1f81a64 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -235,6 +235,7 @@
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.SurfaceCallbackHelper;
+import com.android.modules.expresslog.Counter;
import com.android.window.flags.Flags;
import java.io.IOException;
@@ -252,7 +253,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
-
/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
@@ -828,6 +828,8 @@
private boolean mInsetsAnimationRunning;
private long mPreviousFrameDrawnTime = -1;
+ // The largest view size percentage to the display size. Used on trace to collect metric.
+ private float mLargestChildPercentage = 0.0f;
/**
* The resolved pointer icon type requested by this window.
@@ -1067,6 +1069,7 @@
private String mTag = TAG;
private String mFpsTraceName;
+ private String mLargestViewTraceName;
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1318,6 +1321,7 @@
attrs = mWindowAttributes;
setTag();
mFpsTraceName = "FPS of " + getTitle();
+ mLargestViewTraceName = "Largest view percentage(per hundred) of " + getTitle();
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
@@ -4739,6 +4743,10 @@
long fps = NANOS_PER_SEC / timeDiff;
Trace.setCounter(mFpsTraceName, fps);
mPreviousFrameDrawnTime = expectedDrawnTime;
+
+ long percentage = (long) (mLargestChildPercentage * 100);
+ Trace.setCounter(mLargestViewTraceName, percentage);
+ mLargestChildPercentage = 0.0f;
}
private void reportDrawFinished(@Nullable Transaction t, int seqId) {
@@ -5059,6 +5067,7 @@
if (DEBUG_FPS) {
trackFPS();
}
+
if (sToolkitMetricsForFrameRateDecisionFlagValue) {
collectFrameRateDecisionMetrics();
}
@@ -9696,6 +9705,9 @@
} else {
q.mReceiver.finishInputEvent(q.mEvent, handled);
}
+ if (q.mEvent instanceof KeyEvent) {
+ logHandledSystemKey((KeyEvent) q.mEvent, handled);
+ }
} else {
q.mEvent.recycleIfNeededAfterDispatch();
}
@@ -9703,6 +9715,19 @@
recycleQueuedInputEvent(q);
}
+ private void logHandledSystemKey(KeyEvent event, boolean handled) {
+ final int keyCode = event.getKeyCode();
+ if (keyCode != KeyEvent.KEYCODE_STEM_PRIMARY) {
+ return;
+ }
+ if (event.isDown() && event.getRepeatCount() == 0 && handled) {
+ // Initial DOWN event is handled. Log the stem primary key press.
+ Counter.logIncrementWithUid(
+ "input.value_app_handled_stem_primary_key_gestures_count",
+ Process.myUid());
+ }
+ }
+
static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
@@ -12148,7 +12173,8 @@
|| motionEventAction == MotionEvent.ACTION_UP;
boolean undesiredType = windowType == TYPE_INPUT_METHOD;
// use toolkitSetFrameRate flag to gate the change
- return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue;
+ return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue
+ && getFrameRateBoostOnTouchEnabled();
}
/**
@@ -12223,6 +12249,15 @@
return mIsFrameRateBoosting;
}
+ /**
+ * Get the value of mFrameRateBoostOnTouchEnabled
+ * Can be used to checked if touch boost is enabled. The default value is true.
+ */
+ @VisibleForTesting
+ public boolean getFrameRateBoostOnTouchEnabled() {
+ return mWindowAttributes.getFrameRateBoostOnTouchEnabled();
+ }
+
private void boostFrameRate(int boostTimeOut) {
mIsFrameRateBoosting = true;
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
@@ -12252,4 +12287,10 @@
void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) {
mWindowlessBackKeyCallback = callback;
}
+
+ void recordViewPercentage(float percentage) {
+ if (!Trace.isEnabled()) return;
+ // Record the largest view of percentage to the display size.
+ mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage);
+ }
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 87537fbc..7bae7ec 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -334,6 +334,9 @@
private boolean mOverlayWithDecorCaptionEnabled = true;
private boolean mCloseOnSwipeEnabled = false;
+ private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
+ android.view.flags.Flags.toolkitSetFrameRateReadOnly();
+
// The current window attributes.
@UnsupportedAppUsage
private final WindowManager.LayoutParams mWindowAttributes =
@@ -1373,6 +1376,39 @@
}
/**
+ * Sets whether the frame rate touch boost is enabled for this Window.
+ * When enabled, the frame rate will be boosted when a user touches the Window.
+ *
+ * @param enabled whether the frame rate touch boost is enabled.
+ * @see #getFrameRateBoostOnTouchEnabled()
+ * @see WindowManager.LayoutParams#setFrameRateBoostOnTouchEnabled(boolean)
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setFrameRateBoostOnTouchEnabled(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ final WindowManager.LayoutParams attrs = getAttributes();
+ attrs.setFrameRateBoostOnTouchEnabled(enabled);
+ dispatchWindowAttributesChanged(attrs);
+ }
+ }
+
+ /**
+ * Get whether frame rate touch boost is enabled
+ * {@link #setFrameRateBoostOnTouchEnabled(boolean)}
+ *
+ * @return whether the frame rate touch boost is enabled.
+ * @see #setFrameRateBoostOnTouchEnabled(boolean)
+ * @see WindowManager.LayoutParams#getFrameRateBoostOnTouchEnabled()
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public boolean getFrameRateBoostOnTouchEnabled() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return getAttributes().getFrameRateBoostOnTouchEnabled();
+ }
+ return true;
+ }
+
+ /**
* If {@code isPreferred} is true, this method requests that the connected display does minimal
* post processing when this window is visible on the screen. Otherwise, it requests that the
* display switches back to standard image processing.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 07a347a..f76822f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -4332,6 +4332,13 @@
private float mDesiredHdrHeadroom = 0;
/**
+ * For variable refresh rate project.
+ */
+ private boolean mFrameRateBoostOnTouch = true;
+ private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
+ android.view.flags.Flags.toolkitSetFrameRateReadOnly();
+
+ /**
* Carries the requests about {@link WindowInsetsController.Appearance} and
* {@link WindowInsetsController.Behavior} to the system windows which can produce insets.
*
@@ -4766,6 +4773,32 @@
}
/**
+ * Set the value whether we should enable Touch Boost
+ *
+ * @param enabled Whether we should enable Touch Boost
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setFrameRateBoostOnTouchEnabled(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mFrameRateBoostOnTouch = enabled;
+ }
+ }
+
+ /**
+ * Get the value whether we should enable touch boost as set
+ * by {@link #setFrameRateBoostOnTouchEnabled(boolean)}
+ *
+ * @return A boolean value to indicate whether we should enable touch boost
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public boolean getFrameRateBoostOnTouchEnabled() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return mFrameRateBoostOnTouch;
+ }
+ return true;
+ }
+
+ /**
* <p>
* Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount},
* but instead of dimmed, the content behind the window will be blurred (or combined with
@@ -4916,6 +4949,9 @@
out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
out.writeInt(mDisplayFlags);
out.writeFloat(mDesiredHdrHeadroom);
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ out.writeBoolean(mFrameRateBoostOnTouch);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -4988,6 +5024,9 @@
paramsForRotation = in.createTypedArray(LayoutParams.CREATOR);
mDisplayFlags = in.readInt();
mDesiredHdrHeadroom = in.readFloat();
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mFrameRateBoostOnTouch = in.readBoolean();
+ }
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -5324,6 +5363,12 @@
changes |= LAYOUT_CHANGED;
}
+ if (sToolkitSetFrameRateReadOnlyFlagValue
+ && mFrameRateBoostOnTouch != o.mFrameRateBoostOnTouch) {
+ mFrameRateBoostOnTouch = o.mFrameRateBoostOnTouch;
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -5546,6 +5591,11 @@
sb.append(prefix).append(" forciblyShownTypes=").append(
WindowInsets.Type.toString(forciblyShownTypes));
}
+ if (sToolkitSetFrameRateReadOnlyFlagValue && mFrameRateBoostOnTouch) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" frameRateBoostOnTouch=");
+ sb.append(mFrameRateBoostOnTouch);
+ }
if (paramsForRotation != null && paramsForRotation.length != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" paramsForRotation:");
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 53aed49..49a2843 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -5400,13 +5400,10 @@
public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT =
new AccessibilityAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content forward.
*
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
* this element should also add the relevant directional scroll actions of
@@ -5447,12 +5444,10 @@
public static final AccessibilityAction ACTION_SCROLL_FORWARD =
new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content backward.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
* this element should also add the relevant directional scroll actions of
@@ -5647,48 +5642,40 @@
@NonNull public static final AccessibilityAction ACTION_SCROLL_IN_DIRECTION =
new AccessibilityAction(R.id.accessibilityActionScrollInDirection);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content up.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public static final AccessibilityAction ACTION_SCROLL_UP =
new AccessibilityAction(R.id.accessibilityActionScrollUp);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content left.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public static final AccessibilityAction ACTION_SCROLL_LEFT =
new AccessibilityAction(R.id.accessibilityActionScrollLeft);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content down.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public static final AccessibilityAction ACTION_SCROLL_DOWN =
new AccessibilityAction(R.id.accessibilityActionScrollDown);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content right.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index b3359b7..70d8abe 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -23,9 +23,6 @@
import android.annotation.FlaggedApi;
import android.annotation.InterpolatorRes;
import android.annotation.TestApi;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
-import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -55,18 +52,6 @@
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
- /**
- * For apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and above,
- * this change ID enables to use expectedPresentationTime instead of the frameTime
- * for the frame start time .
- *
- * @hide
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
- @Overridable
- public static final long OVERRIDE_ENABLE_EXPECTED_PRSENTATION_TIME = 278730197L;
-
private static boolean sExpectedPresentationTimeFlagValue;
static {
sExpectedPresentationTimeFlagValue = expectedPresentationTimeReadOnly();
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 5bfa3d7..7c9340e 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -165,6 +165,9 @@
public static final int FLAGS_IS_NON_APP_WINDOW =
FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD | FLAG_IS_SYSTEM_WINDOW;
+ /** The change will not participate in the animation. */
+ public static final int FLAGS_IS_OCCLUDED_NO_ANIMATION = FLAG_IS_OCCLUDED | FLAG_NO_ANIMATION;
+
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
diff --git a/core/java/com/android/internal/pm/parsing/IPackageCacher.java b/core/java/com/android/internal/pm/parsing/IPackageCacher.java
new file mode 100644
index 0000000..3e01730
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/IPackageCacher.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.pm.parsing;
+
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+
+import java.io.File;
+
+/** @hide */
+public interface IPackageCacher {
+
+ /**
+ * Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
+ * or {@code null} if no cached result exists.
+ */
+ ParsedPackage getCachedResult(File packageFile, int flags);
+
+ /**
+ * Caches the parse result for {@code packageFile} with flags {@code flags}.
+ */
+ void cacheResult(File packageFile, int flags, ParsedPackage parsed);
+}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/core/java/com/android/internal/pm/parsing/PackageParser2.java
similarity index 69%
rename from services/core/java/com/android/server/pm/parsing/PackageParser2.java
rename to core/java/com/android/internal/pm/parsing/PackageParser2.java
index b6a08a5..e413293 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/core/java/com/android/internal/pm/parsing/PackageParser2.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package com.android.server.pm.parsing;
+package com.android.internal.pm.parsing;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
@@ -28,26 +27,20 @@
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.TypedArray;
import android.os.Build;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.permission.PermissionManager;
import android.util.DisplayMetrics;
import android.util.Slog;
-import com.android.internal.compat.IPlatformCompat;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.pm.pkg.parsing.ParsingUtils;
import com.android.internal.util.ArrayUtils;
-import com.android.server.SystemConfig;
-import com.android.server.pm.PackageManagerException;
-import com.android.server.pm.PackageManagerService;
import java.io.File;
import java.util.List;
-import java.util.Set;
/**
* The v2 of package parsing for use when parsing is initiated in the server and must
@@ -59,50 +52,6 @@
*/
public class PackageParser2 implements AutoCloseable {
- /**
- * For parsing inside the system server but outside of {@link PackageManagerService}.
- * Generally used for parsing information in an APK that hasn't been installed yet.
- *
- * This must be called inside the system process as it relies on {@link ServiceManager}.
- */
- @NonNull
- public static PackageParser2 forParsingFileWithDefaults() {
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
- return new PackageParser2(null /* separateProcesses */, null /* displayMetrics */,
- null /* cacheDir */, new Callback() {
- @Override
- public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
- try {
- return platformCompat.isChangeEnabled(changeId, appInfo);
- } catch (Exception e) {
- // This shouldn't happen, but assume enforcement if it does
- Slog.wtf(TAG, "IPlatformCompat query failed", e);
- return true;
- }
- }
-
- @Override
- public boolean hasFeature(String feature) {
- // Assume the device doesn't support anything. This will affect permission parsing
- // and will force <uses-permission/> declarations to include all requiredNotFeature
- // permissions and exclude all requiredFeature permissions. This mirrors the old
- // behavior.
- return false;
- }
-
- @Override
- public Set<String> getHiddenApiWhitelistedApps() {
- return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
- }
-
- @Override
- public Set<String> getInstallConstraintsAllowlist() {
- return SystemConfig.getInstance().getInstallConstraintsAllowlist();
- }
- });
- }
-
private static final String TAG = ParsingUtils.TAG;
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
@@ -118,12 +67,12 @@
private final ThreadLocal<ParseTypeImpl> mSharedResult;
@Nullable
- protected PackageCacher mCacher;
+ protected IPackageCacher mCacher;
- private final ParsingPackageUtils parsingUtils;
+ private final ParsingPackageUtils mParsingUtils;
public PackageParser2(String[] separateProcesses, DisplayMetrics displayMetrics,
- @Nullable File cacheDir, @NonNull Callback callback) {
+ @Nullable IPackageCacher cacher, @NonNull Callback callback) {
if (displayMetrics == null) {
displayMetrics = new DisplayMetrics();
displayMetrics.setToDefaults();
@@ -134,9 +83,9 @@
List<PermissionManager.SplitPermissionInfo> splitPermissions = permissionManager
.getSplitPermissions();
- mCacher = cacheDir == null ? null : new PackageCacher(cacheDir);
+ mCacher = cacher;
- parsingUtils = new ParsingPackageUtils(separateProcesses, displayMetrics, splitPermissions,
+ mParsingUtils = new ParsingPackageUtils(separateProcesses, displayMetrics, splitPermissions,
callback);
ParseInput.Callback enforcementCallback = (changeId, packageName, targetSdkVersion) -> {
@@ -155,7 +104,7 @@
*/
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
- throws PackageManagerException {
+ throws PackageParserException {
var files = packageFile.listFiles();
// Apk directory is directly nested under the current directory
if (ArrayUtils.size(files) == 1 && files[0].isDirectory()) {
@@ -171,9 +120,9 @@
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
ParseInput input = mSharedResult.get().reset();
- ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
+ ParseResult<ParsingPackage> result = mParsingUtils.parsePackage(input, packageFile, flags);
if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
@@ -201,12 +150,12 @@
*/
@AnyThread
public ParsedPackage parsePackageFromPackageLite(PackageLite packageLite, int flags)
- throws PackageManagerException {
+ throws PackageParserException {
ParseInput input = mSharedResult.get().reset();
- ParseResult<ParsingPackage> result = parsingUtils.parsePackageFromPackageLite(input,
+ ParseResult<ParsingPackage> result = mParsingUtils.parsePackageFromPackageLite(input,
packageLite, flags);
if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
return result.getResult().hideAsParsed();
@@ -226,7 +175,7 @@
mSharedAppInfo.remove();
}
- public static abstract class Callback implements ParsingPackageUtils.Callback {
+ public abstract static class Callback implements ParsingPackageUtils.Callback {
@Override
public final ParsingPackage startParsingPackage(@NonNull String packageName,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d0de5f0..5400c58 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6862,4 +6862,8 @@
<!-- Whether the media player is shown on the quick settings -->
<bool name="config_quickSettingsShowMediaPlayer">true</bool>
+
+ <!-- Defines suitability of the built-in speaker route.
+ Refer to {@link MediaRoute2Info} to see supported values. -->
+ <integer name="config_mediaRouter_builtInSpeakerSuitability">0</integer>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3894330..eeef192 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5306,4 +5306,7 @@
<java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" />
<java-symbol type="bool" name="config_quickSettingsShowMediaPlayer" />
+
+ <!-- Android MediaRouter framework configs. -->
+ <java-symbol type="integer" name="config_mediaRouter_builtInSpeakerSuitability" />
</resources>
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index b30a0c8..cf3eb12 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -659,8 +659,6 @@
ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
view.invalidate();
- assertEquals(viewRootImpl.getLastPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NORMAL);
viewRootImpl.notifyInsetsAnimationRunningStateChanged(true);
view.invalidate();
});
@@ -672,6 +670,37 @@
});
}
+
+ /**
+ * Test FrameRateBoostOnTouchEnabled API
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_frameRateBoostOnTouch() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ final WindowManager.LayoutParams attrs = viewRootImpl.mWindowAttributes;
+ assertEquals(attrs.getFrameRateBoostOnTouchEnabled(), true);
+ assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
+ attrs.getFrameRateBoostOnTouchEnabled());
+
+ sInstrumentation.runOnMainSync(() -> {
+ attrs.setFrameRateBoostOnTouchEnabled(false);
+ viewRootImpl.setLayoutParams(attrs, false);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ sInstrumentation.runOnMainSync(() -> {
+ final WindowManager.LayoutParams newAttrs = viewRootImpl.mWindowAttributes;
+ assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(), false);
+ assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
+ newAttrs.getFrameRateBoostOnTouchEnabled());
+ });
+ }
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java
deleted file mode 100644
index ff49cdc..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions.embedding;
-
-import static java.util.Objects.requireNonNull;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-/**
- * The parameter to create an overlay container that retrieved from
- * {@link android.app.ActivityOptions} bundle.
- */
-class OverlayCreateParams {
-
- // TODO(b/295803704): Move them to WM Extensions so that we can reuse in WM Jetpack.
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS =
- "androidx.window.extensions.OverlayCreateParams";
-
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS_TASK_ID =
- "androidx.window.extensions.OverlayCreateParams.taskId";
-
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS_TAG =
- "androidx.window.extensions.OverlayCreateParams.tag";
-
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS_BOUNDS =
- "androidx.window.extensions.OverlayCreateParams.bounds";
-
- private final int mTaskId;
-
- @NonNull
- private final String mTag;
-
- @NonNull
- private final Rect mBounds;
-
- OverlayCreateParams(int taskId, @NonNull String tag, @NonNull Rect bounds) {
- mTaskId = taskId;
- mTag = requireNonNull(tag);
- mBounds = requireNonNull(bounds);
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- @NonNull
- String getTag() {
- return mTag;
- }
-
- @NonNull
- Rect getBounds() {
- return mBounds;
- }
-
- @Override
- public int hashCode() {
- int result = mTaskId;
- result = 31 * result + mTag.hashCode();
- result = 31 * result + mBounds.hashCode();
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) return true;
- if (!(obj instanceof OverlayCreateParams thatParams)) return false;
- return mTaskId == thatParams.mTaskId
- && mTag.equals(thatParams.mTag)
- && mBounds.equals(thatParams.mBounds);
- }
-
- @Override
- public String toString() {
- return OverlayCreateParams.class.getSimpleName() + ": {"
- + "taskId=" + mTaskId
- + ", tag=" + mTag
- + ", bounds=" + mBounds
- + "}";
- }
-
- /** Retrieves the {@link OverlayCreateParams} from {@link android.app.ActivityOptions} bundle */
- @Nullable
- static OverlayCreateParams fromBundle(@NonNull Bundle bundle) {
- final Bundle paramsBundle = bundle.getBundle(KEY_OVERLAY_CREATE_PARAMS);
- if (paramsBundle == null) {
- return null;
- }
- final int taskId = paramsBundle.getInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID);
- final String tag = requireNonNull(paramsBundle.getString(KEY_OVERLAY_CREATE_PARAMS_TAG));
- final Rect bounds = requireNonNull(paramsBundle.getParcelable(
- KEY_OVERLAY_CREATE_PARAMS_BOUNDS, Rect.class));
-
- return new OverlayCreateParams(taskId, tag, bounds);
- }
-}
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 4973a4d..15ee4e1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -34,6 +34,7 @@
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
@@ -136,6 +137,15 @@
private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;
/**
+ * A calculator function to compute {@link ActivityStack} attributes in a task, which is called
+ * when there's {@link #onTaskFragmentParentInfoChanged} or folding state changed.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
+ mActivityStackAttributesCalculator;
+
+ /**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
* below it.
* When the app is host of multiple Tasks, there can be multiple splits controlled by the same
@@ -319,6 +329,22 @@
}
}
+ @Override
+ public void setActivityStackAttributesCalculator(
+ @NonNull Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
+ calculator) {
+ synchronized (mLock) {
+ mActivityStackAttributesCalculator = calculator;
+ }
+ }
+
+ @Override
+ public void clearActivityStackAttributesCalculator() {
+ synchronized (mLock) {
+ mActivityStackAttributesCalculator = null;
+ }
+ }
+
@GuardedBy("mLock")
@Nullable
Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
@@ -1412,7 +1438,7 @@
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@Nullable Activity launchingActivity) {
return createEmptyContainer(wct, intent, taskId, new Rect(), launchingActivity,
- null /* overlayTag */);
+ null /* overlayTag */, null /* launchOptions */);
}
/**
@@ -1426,7 +1452,7 @@
TaskFragmentContainer createEmptyContainer(
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@NonNull Rect bounds, @Nullable Activity launchingActivity,
- @Nullable String overlayTag) {
+ @Nullable String overlayTag, @Nullable Bundle launchOptions) {
// We need an activity in the organizer process in the same Task to use as the owner
// activity, as well as to get the Task window info.
final Activity activityInTask;
@@ -1443,7 +1469,8 @@
return null;
}
final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
- intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag);
+ intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag,
+ launchOptions);
final IBinder taskFragmentToken = container.getTaskFragmentToken();
// Note that taskContainer will not exist before calling #newContainer if the container
// is the first embedded TF in the task.
@@ -1570,14 +1597,16 @@
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
@NonNull Activity activityInTask, int taskId) {
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
- activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
+ activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
+ null /* launchOptions */);
}
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
@NonNull Activity activityInTask, int taskId) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
+ activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
+ null /* launchOptions */);
}
@GuardedBy("mLock")
@@ -1585,7 +1614,8 @@
@NonNull Activity activityInTask, int taskId,
@NonNull TaskFragmentContainer pairedPrimaryContainer) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, pairedPrimaryContainer, null /* tag */);
+ activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
+ null /* launchOptions */);
}
/**
@@ -1602,11 +1632,14 @@
* @param overlayTag The tag for the new created overlay container. It must be
* needed if {@code isOverlay} is {@code true}. Otherwise,
* it should be {@code null}.
+ * @param launchOptions The launch options bundle to create a container. Must be
+ * specified for overlay container.
*/
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
- @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
+ @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
+ @Nullable Bundle launchOptions) {
if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,");
}
@@ -1615,7 +1648,8 @@
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
- pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag);
+ pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag,
+ launchOptions);
return container;
}
@@ -2345,28 +2379,28 @@
@GuardedBy("mLock")
@Nullable
TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
- @NonNull WindowContainerTransaction wct,
- @NonNull OverlayCreateParams overlayCreateParams, int launchTaskId,
+ @NonNull WindowContainerTransaction wct, @NonNull Bundle options,
@NonNull Intent intent, @NonNull Activity launchActivity) {
- final int taskId = overlayCreateParams.getTaskId();
- if (taskId != launchTaskId) {
- // The task ID doesn't match the launch activity's. Cannot determine the host task
- // to launch the overlay.
- throw new IllegalArgumentException("The task ID of "
- + "OverlayCreateParams#launchingActivity must match the task ID of "
- + "the activity to #startActivity with the activity options that takes "
- + "OverlayCreateParams.");
- }
final List<TaskFragmentContainer> overlayContainers =
getAllOverlayTaskFragmentContainers();
- final String overlayTag = overlayCreateParams.getTag();
+ final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG));
// If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
// specified by Intent, expand the overlay container to fill the parent task instead.
- final Rect bounds = overlayCreateParams.getBounds();
- final Size minDimensions = getMinDimensions(intent);
- final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(bounds,
- minDimensions);
+ final ActivityStackAttributesCalculatorParams params =
+ new ActivityStackAttributesCalculatorParams(mPresenter.toParentContainerInfo(
+ mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
+ // Fallback to expand the bounds if there's no activityStackAttributes calculator.
+ final Rect relativeBounds = mActivityStackAttributesCalculator != null
+ ? new Rect(mActivityStackAttributesCalculator.apply(params).getRelativeBounds())
+ : new Rect();
+ final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(relativeBounds,
+ getMinDimensions(intent));
+ // Expand the bounds if the requested bounds are smaller than minimum dimensions.
+ if (shouldExpandContainer) {
+ relativeBounds.setEmpty();
+ }
+ final int taskId = getTaskId(launchActivity);
if (!overlayContainers.isEmpty()) {
for (final TaskFragmentContainer overlayContainer : overlayContainers) {
if (!overlayTag.equals(overlayContainer.getOverlayTag())
@@ -2390,7 +2424,7 @@
final Rect taskBounds = overlayContainer.getTaskContainer().getTaskProperties()
.getTaskMetrics().getBounds();
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
- final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
+ final Rect sanitizedBounds = sanitizeBounds(relativeBounds, intent, taskBounds);
mPresenter.resizeTaskFragment(wct, overlayToken, sanitizedBounds);
mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayToken,
!sanitizedBounds.isEmpty());
@@ -2402,8 +2436,9 @@
}
}
}
- return createEmptyContainer(wct, intent, taskId,
- (shouldExpandContainer ? new Rect() : bounds), launchActivity, overlayTag);
+ // Launch the overlay container to the task with taskId.
+ return createEmptyContainer(wct, intent, taskId, relativeBounds, launchActivity, overlayTag,
+ options);
}
private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@@ -2568,12 +2603,11 @@
final TaskFragmentContainer launchedInTaskFragment;
if (launchingActivity != null) {
final int taskId = getTaskId(launchingActivity);
- final OverlayCreateParams overlayCreateParams =
- OverlayCreateParams.fromBundle(options);
+ final String overlayTag = options.getString(KEY_OVERLAY_TAG);
if (Flags.activityEmbeddingOverlayPresentationFlag()
- && overlayCreateParams != null) {
+ && overlayTag != null) {
launchedInTaskFragment = createOrUpdateOverlayTaskFragmentIfNeeded(wct,
- overlayCreateParams, taskId, intent, launchingActivity);
+ options, intent, launchingActivity);
} else {
launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
launchingActivity);
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 b5c32bb..acfd8e4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -1084,4 +1084,14 @@
WindowMetrics getTaskWindowMetrics(@NonNull Activity activity) {
return getTaskProperties(activity).getTaskMetrics();
}
+
+ @NonNull
+ ParentContainerInfo toParentContainerInfo(@NonNull TaskProperties taskProperties) {
+ final Configuration configuration = taskProperties.getConfiguration();
+ final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
+ .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
+ configuration.windowConfiguration);
+ return new ParentContainerInfo(taskProperties.getTaskMetrics(), configuration,
+ windowLayoutInfo);
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index ed8c4f3..da87339 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.util.Size;
import android.window.TaskFragmentAnimationParams;
@@ -105,6 +106,13 @@
@Nullable
private final String mOverlayTag;
+ /**
+ * The launch options that was used to create this container. Must not be {@code null} for
+ * {@link #isOverlay()} container.
+ */
+ @Nullable
+ private final Bundle mLaunchOptions;
+
/** Indicates whether the container was cleaned up after the last activity was removed. */
private boolean mIsFinished;
@@ -165,7 +173,7 @@
/**
* @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
- * TaskFragmentContainer, String)
+ * TaskFragmentContainer, String, Bundle)
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent,
@@ -173,7 +181,8 @@
@NonNull SplitController controller,
@Nullable TaskFragmentContainer pairedPrimaryContainer) {
this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
- controller, pairedPrimaryContainer, null /* overlayTag */);
+ controller, pairedPrimaryContainer, null /* overlayTag */,
+ null /* launchOptions */);
}
/**
@@ -181,11 +190,14 @@
* container transaction.
* @param pairedPrimaryContainer when it is set, the new container will be add right above it
* @param overlayTag Sets to indicate this taskFragment is an overlay container
+ * @param launchOptions The launch options to create this container. Must not be
+ * {@code null} for an overlay container
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller,
- @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
+ @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
+ @Nullable Bundle launchOptions) {
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
throw new IllegalArgumentException(
@@ -195,6 +207,10 @@
mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
mOverlayTag = overlayTag;
+ if (overlayTag != null) {
+ Objects.requireNonNull(launchOptions);
+ }
+ mLaunchOptions = launchOptions;
if (pairedPrimaryContainer != null) {
// The TaskFragment will be positioned right above the paired container.
@@ -903,8 +919,8 @@
}
/**
- * Returns the tag specified in {@link OverlayCreateParams#getTag()}. {@code null} if this
- * taskFragment container is not an overlay container.
+ * Returns the tag specified in launch options. {@code null} if this taskFragment container is
+ * not an overlay container.
*/
@Nullable
String getOverlayTag() {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 4c2433f..678bdef 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -18,14 +18,11 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
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.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_BOUNDS;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TAG;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TASK_ID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -45,6 +42,7 @@
import static org.mockito.Mockito.never;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -98,9 +96,6 @@
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
- private static final OverlayCreateParams TEST_OVERLAY_CREATE_PARAMS =
- new OverlayCreateParams(TASK_ID, "test,", new Rect(0, 0, 200, 200));
-
private SplitController.ActivityStartMonitor mMonitor;
private Intent mIntent;
@@ -165,37 +160,15 @@
}
@Test
- public void testOverlayCreateParamsFromBundle() {
- assertThat(OverlayCreateParams.fromBundle(new Bundle())).isNull();
-
- assertThat(OverlayCreateParams.fromBundle(createOverlayCreateParamsTestBundle()))
- .isEqualTo(TEST_OVERLAY_CREATE_PARAMS);
- }
-
- @Test
public void testStartActivity_overlayFeatureDisabled_notInvokeCreateOverlayContainer() {
mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
- mMonitor.onStartActivity(mActivity, mIntent, createOverlayCreateParamsTestBundle());
+ final Bundle optionsBundle = ActivityOptions.makeBasic().toBundle();
+ optionsBundle.putString(KEY_OVERLAY_TAG, "test");
+ mMonitor.onStartActivity(mActivity, mIntent, optionsBundle);
verify(mSplitController, never()).createOrUpdateOverlayTaskFragmentIfNeeded(any(), any(),
- anyInt(), any(), any());
- }
-
- @NonNull
- private static Bundle createOverlayCreateParamsTestBundle() {
- final Bundle bundle = new Bundle();
-
- final Bundle paramsBundle = new Bundle();
- paramsBundle.putInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID,
- TEST_OVERLAY_CREATE_PARAMS.getTaskId());
- paramsBundle.putString(KEY_OVERLAY_CREATE_PARAMS_TAG, TEST_OVERLAY_CREATE_PARAMS.getTag());
- paramsBundle.putObject(KEY_OVERLAY_CREATE_PARAMS_BOUNDS,
- TEST_OVERLAY_CREATE_PARAMS.getBounds());
-
- bundle.putBundle(KEY_OVERLAY_CREATE_PARAMS, paramsBundle);
-
- return bundle;
+ any(), any());
}
@Test
@@ -221,19 +194,11 @@
}
@Test
- public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_taskIdNotMatch_throwException() {
- assertThrows("The method must return null due to task mismatch between"
- + " launchingActivity and OverlayCreateParams", IllegalArgumentException.class,
- () -> createOrUpdateOverlayTaskFragmentIfNeeded(
- TEST_OVERLAY_CREATE_PARAMS, TASK_ID + 1));
- }
-
- @Test
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask_dismissOverlay() {
createExistingOverlayContainers();
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID, "test3", new Rect(0, 0, 100, 100)), TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test3");
assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ " is launched to the same task")
@@ -245,9 +210,9 @@
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_sameTagAnotherTask_dismissOverlay() {
createExistingOverlayContainers();
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID + 2, "test1", new Rect(0, 0, 100, 100)),
- TASK_ID + 2);
+ doReturn(TASK_ID + 2).when(mActivity).getTaskId();
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test1");
assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ " is launched with the same tag as an existing overlay container in a different "
@@ -261,9 +226,10 @@
createExistingOverlayContainers();
final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID, "test1", bounds),
- TASK_ID);
+ "test1");
assertWithMessage("overlayContainer1 must be updated since the new overlay container"
+ " is launched with the same tag and task")
@@ -279,9 +245,8 @@
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissMultipleOverlays() {
createExistingOverlayContainers();
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID, "test2", new Rect(0, 0, 100, 100)),
- TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test2");
// OverlayContainer1 is dismissed since new container is launched in the same task with
// different tag. OverlayContainer2 is dismissed since new container is launched with the
@@ -304,8 +269,11 @@
mIntent.setComponent(new ComponentName(ApplicationProvider.getApplicationContext(),
MinimumDimensionActivity.class));
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
@@ -316,7 +284,7 @@
// Call createOrUpdateOverlayTaskFragmentIfNeeded again to check the update case.
clearInvocations(mSplitPresenter);
- createOrUpdateOverlayTaskFragmentIfNeeded(TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
@@ -329,11 +297,11 @@
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_notInTaskBounds_expandOverlay() {
final Rect bounds = new Rect(TASK_BOUNDS);
bounds.offset(10, 10);
- final OverlayCreateParams paramsOutsideTaskBounds = new OverlayCreateParams(TASK_ID,
- "test", bounds);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- paramsOutsideTaskBounds, TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
@@ -344,7 +312,7 @@
// Call createOrUpdateOverlayTaskFragmentIfNeeded again to check the update case.
clearInvocations(mSplitPresenter);
- createOrUpdateOverlayTaskFragmentIfNeeded(paramsOutsideTaskBounds, TASK_ID);
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
@@ -355,15 +323,17 @@
@Test
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_createOverlay() {
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
.containsExactly(overlayContainer);
assertThat(overlayContainer.getTaskId()).isEqualTo(TASK_ID);
- assertThat(overlayContainer
- .areLastRequestedBoundsEqual(TEST_OVERLAY_CREATE_PARAMS.getBounds())).isTrue();
- assertThat(overlayContainer.getOverlayTag()).isEqualTo(TEST_OVERLAY_CREATE_PARAMS.getTag());
+ assertThat(overlayContainer.areLastRequestedBoundsEqual(bounds)).isTrue();
+ assertThat(overlayContainer.getOverlayTag()).isEqualTo("test");
}
@Test
@@ -416,12 +386,14 @@
@Test
public void testGetTopNonFinishingActivityWithOverlay() {
- createTestOverlayContainer(TASK_ID, "test1");
+ TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test1");
+
final Activity activity = createMockActivity();
final TaskFragmentContainer container = createMockTaskFragmentContainer(activity);
final TaskContainer task = container.getTaskContainer();
- assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */)).isEqualTo(mActivity);
+ assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */))
+ .isEqualTo(overlayContainer.getTopNonFinishingActivity());
assertThat(task.getTopNonFinishingActivity(false /* includeOverlay */)).isEqualTo(activity);
}
@@ -458,10 +430,11 @@
* #createOrUpdateOverlayTaskFragmentIfNeeded}
*/
@Nullable
- private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
- @NonNull OverlayCreateParams params, int taskId) {
- return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction, params,
- taskId, mIntent, mActivity);
+ private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(@NonNull String tag) {
+ final Bundle launchOptions = new Bundle();
+ launchOptions.putString(KEY_OVERLAY_TAG, tag);
+ return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction,
+ launchOptions, mIntent, mActivity);
}
/** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
@@ -475,10 +448,11 @@
@NonNull
private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) {
+ Activity activity = createMockActivity();
TaskFragmentContainer overlayContainer = mSplitController.newContainer(
- null /* pendingAppearedActivity */, mIntent, mActivity, taskId,
- null /* pairedPrimaryContainer */, tag);
- setupTaskFragmentInfo(overlayContainer, mActivity);
+ null /* pendingAppearedActivity */, mIntent, activity, taskId,
+ null /* pairedPrimaryContainer */, tag, Bundle.EMPTY);
+ setupTaskFragmentInfo(overlayContainer, activity);
return overlayContainer;
}
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 8c274a2..bab4e91 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
@@ -590,7 +590,7 @@
assertFalse(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString());
+ anyString(), any());
}
@Test
@@ -753,7 +753,7 @@
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString());
+ anyString(), any());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -796,7 +796,7 @@
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString());
+ anyString(), any());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 4511f3b..901d5fa 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -57,3 +57,10 @@
description: "Enables left/right split in portrait"
bug: "291018646"
}
+
+flag {
+ name: "enable_new_bubble_animations"
+ namespace: "multitasking"
+ description: "Enables new animations for expand and collapse for bubbles"
+ bug: "311450609"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index f794fef..893a87f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -48,12 +48,14 @@
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100;
+ private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
+ private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
+ private static final int EXPANDED_VIEW_DRAG_ANIMATION_DURATION = 150;
/**
* Additional scale applied to expanded view when it is positioned inside a magnetic target.
*/
- private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f;
- private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
- private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
+ private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.6f;
+ private static final float EXPANDED_VIEW_DRAG_SCALE = 0.5f;
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -72,6 +74,7 @@
private final Context mContext;
private final BubbleBarLayerView mLayerView;
private final BubblePositioner mPositioner;
+ private final int[] mTmpLocation = new int[2];
private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded = false;
@@ -220,6 +223,25 @@
}
/**
+ * Animate the expanded bubble when it is being dragged
+ */
+ public void animateStartDrag() {
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ Log.w(TAG, "Trying to animate start drag without a bubble");
+ return;
+ }
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(0f);
+ bbev.animate()
+ .scaleX(EXPANDED_VIEW_DRAG_SCALE)
+ .scaleY(EXPANDED_VIEW_DRAG_SCALE)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION)
+ .start();
+ }
+
+ /**
* Animates dismissal of currently expanded bubble
*
* @param endRunnable a runnable to run at the end of the animation
@@ -261,7 +283,10 @@
.setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION)
.setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
.withStartAction(() -> bbev.setAnimating(true))
- .withEndAction(() -> bbev.setAnimating(false))
+ .withEndAction(() -> {
+ bbev.setAnimating(false);
+ bbev.resetPivot();
+ })
.start();
}
@@ -277,25 +302,52 @@
Log.w(TAG, "Trying to snap the expanded view to target without a bubble");
return;
}
- Point expandedViewCenter = getViewCenterOnScreen(bbev);
-
- // Calculate the difference between the target's center coordinates and the object's.
- // Animating the object's x/y properties by these values will center the object on top
- // of the magnetic target.
- float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x;
- float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y;
// Calculate scale of expanded view so it fits inside the magnetic target
float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
- float targetMaxSide = Math.max(target.getTargetView().getWidth(),
- target.getTargetView().getHeight());
- float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide;
+ View targetView = target.getTargetView();
+ float targetMaxSide = Math.max(targetView.getWidth(), targetView.getHeight());
+ // Reduce target size to have some padding between the target and expanded view
+ targetMaxSide *= EXPANDED_VIEW_IN_TARGET_SCALE;
+ float scaleInTarget = targetMaxSide / bbevMaxSide;
+
+ // Scale around the top center of the expanded view. Same as when dragging.
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(0);
+
+ // When the view animates into the target, it is scaled down with the pivot at center top.
+ // Find the point on the view that would be the center of the view at its final scale.
+ // Once we know that, we can calculate x and y distance from the center of the target view
+ // and use that for the translation animation to ensure that the view at final scale is
+ // placed at the center of the target.
+
+ // Set mTmpLocation to the current location of the view on the screen, taking into account
+ // any scale applied.
+ bbev.getLocationOnScreen(mTmpLocation);
+ // Since pivotX is at the center of the x-axis, even at final scale, center of the view on
+ // x-axis will be the same as the center of the view at current size.
+ // Get scaled width of the view and adjust mTmpLocation so that point on x-axis is at the
+ // center of the view at its current size.
+ float currentWidth = bbev.getWidth() * bbev.getScaleX();
+ mTmpLocation[0] += currentWidth / 2;
+ // Since pivotY is at the top of the view, at final scale, top coordinate of the view
+ // remains the same.
+ // Get height of the view at final scale and adjust mTmpLocation so that point on y-axis is
+ // moved down by half of the height at final scale.
+ float targetHeight = bbev.getHeight() * scaleInTarget;
+ mTmpLocation[1] += targetHeight / 2;
+ // mTmpLocation is now set to the point on the view that will be the center of the view once
+ // scale is applied.
+
+ // Calculate the difference between the target's center coordinates and mTmpLocation
+ float xDiff = target.getCenterOnScreen().x - mTmpLocation[0];
+ float yDiff = target.getCenterOnScreen().y - mTmpLocation[1];
bbev.animate()
.translationX(bbev.getTranslationX() + xDiff)
.translationY(bbev.getTranslationY() + yDiff)
- .scaleX(scale)
- .scaleY(scale)
+ .scaleX(scaleInTarget)
+ .scaleY(scaleInTarget)
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
.setInterpolator(Interpolators.EMPHASIZED)
.withStartAction(() -> bbev.setAnimating(true))
@@ -319,8 +371,8 @@
}
expandedView
.animate()
- .scaleX(1f)
- .scaleY(1f)
+ .scaleX(EXPANDED_VIEW_DRAG_SCALE)
+ .scaleY(EXPANDED_VIEW_DRAG_SCALE)
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
.setInterpolator(Interpolators.EMPHASIZED)
.withStartAction(() -> expandedView.setAnimating(true))
@@ -385,12 +437,4 @@
final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
return new Size(width, height);
}
-
- private Point getViewCenterOnScreen(View view) {
- Point center = new Point();
- int[] onScreenLocation = view.getLocationOnScreen();
- center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f));
- center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f));
- return center;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index d215450..5e634a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -74,6 +74,9 @@
}
private inner class HandleDragListener : RelativeTouchListener() {
+
+ private var isMoving = false
+
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
return !expandedView.isAnimating
@@ -87,6 +90,10 @@
dx: Float,
dy: Float
) {
+ if (!isMoving) {
+ isMoving = true
+ animationHelper.animateStartDrag()
+ }
expandedView.translationX = expandedViewInitialTranslationX + dx
expandedView.translationY = expandedViewInitialTranslationY + dy
dismissView.show()
@@ -114,6 +121,7 @@
animationHelper.animateToRestPosition()
dismissView.hide()
}
+ isMoving = false
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index af69b52..b0d8b47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -757,6 +757,10 @@
}
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
+ } else if (change.hasAllFlags(TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION)) {
+ // Remove the change because it should be invisible in the animation.
+ info.getChanges().remove(i);
+ continue;
}
// The change has already animated by back gesture, don't need to play transition
// animation on it.
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index 29bfd1a..e2dddad 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -19,6 +19,7 @@
import android.media.MediaRoute2Info;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
+import android.os.UserHandle;
/**
* @hide
@@ -35,5 +36,6 @@
* Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result.
*/
void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
- in MediaRoute2Info route);
+ in MediaRoute2Info route, in UserHandle transferInitiatorUserHandle,
+ in String transferInitiatorPackageName);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index fa4d1a1..04e99ea 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -64,7 +64,8 @@
void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
in RoutingSessionInfo oldSession, in MediaRoute2Info route,
- in @nullable Bundle sessionHints);
+ in @nullable Bundle sessionHints, in UserHandle transferInitiatorUserHandle,
+ in String transferInitiatorPackageName);
void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
@@ -84,13 +85,16 @@
void stopScan(IMediaRouter2Manager manager);
void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
- in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
+ in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route,
+ in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName);
void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, in MediaRoute2Info route);
void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, in MediaRoute2Info route);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)")
void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId,
- String sessionId, in MediaRoute2Info route);
+ String sessionId, in MediaRoute2Info route,
+ in UserHandle transferInitiatorUserHandle, String transferInitiatorPackageName);
void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, int volume);
void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 8ad3587..0eabe66 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -19,6 +19,7 @@
import static android.media.MediaRouter2Utils.toUniqueId;
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import android.annotation.FlaggedApi;
@@ -479,6 +480,37 @@
public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
"android.media.route.feature.REMOTE_GROUP_PLAYBACK";
+ /** Indicates the route is always suitable for media playback. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0;
+
+ /**
+ * Indicates that the route is suitable for media playback only after explicit user selection.
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1;
+
+ /** Indicates that the route is never suitable for media playback. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2;
+
+ /**
+ * Route suitability status.
+ *
+ * <p>Signals whether the route is suitable to play media.
+ *
+ * @hide
+ */
+ @IntDef(
+ value = {
+ SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER,
+ SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER,
+ SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public @interface SuitabilityStatus {}
+
private final String mId;
private final CharSequence mName;
private final List<String> mFeatures;
@@ -500,6 +532,7 @@
private final String mProviderId;
private final boolean mIsVisibilityRestricted;
private final Set<String> mAllowedPackages;
+ @SuitabilityStatus private final int mSuitabilityStatus;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
@@ -521,6 +554,7 @@
mProviderId = builder.mProviderId;
mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
mAllowedPackages = builder.mAllowedPackages;
+ mSuitabilityStatus = builder.mSuitabilityStatus;
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -544,6 +578,7 @@
mProviderId = in.readString();
mIsVisibilityRestricted = in.readBoolean();
mAllowedPackages = Set.of(in.createString8Array());
+ mSuitabilityStatus = in.readInt();
}
/**
@@ -778,6 +813,13 @@
|| mAllowedPackages.contains(packageName);
}
+ /** Returns the route suitability status. */
+ @SuitabilityStatus
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public int getSuitabilityStatus() {
+ return mSuitabilityStatus;
+ }
+
/**
* Dumps the current state of the object to the given {@code pw} as a human-readable string.
*
@@ -809,6 +851,7 @@
pw.println(indent + "mProviderId=" + mProviderId);
pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
+ pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
}
private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -861,39 +904,74 @@
&& Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
&& Objects.equals(mProviderId, other.mProviderId)
&& (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
- && Objects.equals(mAllowedPackages, other.mAllowedPackages);
+ && Objects.equals(mAllowedPackages, other.mAllowedPackages)
+ && mSuitabilityStatus == other.mSuitabilityStatus;
}
@Override
public int hashCode() {
// Note: mExtras is not included.
- return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
- mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
- mVolume, mAddress, mDeduplicationIds, mProviderId, mIsVisibilityRestricted,
- mAllowedPackages);
+ return Objects.hash(
+ mId,
+ mName,
+ mFeatures,
+ mType,
+ mIsSystem,
+ mIconUri,
+ mDescription,
+ mConnectionState,
+ mClientPackageName,
+ mPackageName,
+ mVolumeHandling,
+ mVolumeMax,
+ mVolume,
+ mAddress,
+ mDeduplicationIds,
+ mProviderId,
+ mIsVisibilityRestricted,
+ mAllowedPackages,
+ mSuitabilityStatus);
}
@Override
public String toString() {
// Note: mExtras is not printed here.
- StringBuilder result = new StringBuilder()
- .append("MediaRoute2Info{ ")
- .append("id=").append(getId())
- .append(", name=").append(getName())
- .append(", features=").append(getFeatures())
- .append(", iconUri=").append(getIconUri())
- .append(", description=").append(getDescription())
- .append(", connectionState=").append(getConnectionState())
- .append(", clientPackageName=").append(getClientPackageName())
- .append(", volumeHandling=").append(getVolumeHandling())
- .append(", volumeMax=").append(getVolumeMax())
- .append(", volume=").append(getVolume())
- .append(", address=").append(getAddress())
- .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
- .append(", providerId=").append(getProviderId())
- .append(", isVisibilityRestricted=").append(mIsVisibilityRestricted)
- .append(", allowedPackages=").append(String.join(",", mAllowedPackages))
- .append(" }");
+ StringBuilder result =
+ new StringBuilder()
+ .append("MediaRoute2Info{ ")
+ .append("id=")
+ .append(getId())
+ .append(", name=")
+ .append(getName())
+ .append(", features=")
+ .append(getFeatures())
+ .append(", iconUri=")
+ .append(getIconUri())
+ .append(", description=")
+ .append(getDescription())
+ .append(", connectionState=")
+ .append(getConnectionState())
+ .append(", clientPackageName=")
+ .append(getClientPackageName())
+ .append(", volumeHandling=")
+ .append(getVolumeHandling())
+ .append(", volumeMax=")
+ .append(getVolumeMax())
+ .append(", volume=")
+ .append(getVolume())
+ .append(", address=")
+ .append(getAddress())
+ .append(", deduplicationIds=")
+ .append(String.join(",", getDeduplicationIds()))
+ .append(", providerId=")
+ .append(getProviderId())
+ .append(", isVisibilityRestricted=")
+ .append(mIsVisibilityRestricted)
+ .append(", allowedPackages=")
+ .append(String.join(",", mAllowedPackages))
+ .append(", suitabilityStatus=")
+ .append(mSuitabilityStatus)
+ .append(" }");
return result.toString();
}
@@ -923,6 +1001,7 @@
dest.writeString(mProviderId);
dest.writeBoolean(mIsVisibilityRestricted);
dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
+ dest.writeInt(mSuitabilityStatus);
}
private static String getDeviceTypeString(@Type int deviceType) {
@@ -1005,6 +1084,7 @@
private String mProviderId;
private boolean mIsVisibilityRestricted;
private Set<String> mAllowedPackages;
+ @SuitabilityStatus private int mSuitabilityStatus;
/**
* Constructor for builder to create {@link MediaRoute2Info}.
@@ -1028,6 +1108,7 @@
mFeatures = new ArrayList<>();
mDeduplicationIds = Set.of();
mAllowedPackages = Set.of();
+ mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
}
/**
@@ -1075,6 +1156,7 @@
mProviderId = routeInfo.mProviderId;
mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
mAllowedPackages = routeInfo.mAllowedPackages;
+ mSuitabilityStatus = routeInfo.mSuitabilityStatus;
}
/**
@@ -1318,6 +1400,23 @@
}
/**
+ * Sets route suitability status.
+ *
+ * <p>The default value is {@link
+ * MediaRoute2Info#SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER}.
+ *
+ * <p> Apps are not supposed to set {@link
+ * MediaRoute2Info#SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER}. Publishing a non-system
+ * route with such status throws {@link SecurityException}.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public Builder setSuitabilityStatus(@SuitabilityStatus int suitabilityStatus) {
+ mSuitabilityStatus = suitabilityStatus;
+ return this;
+ }
+
+ /**
* Builds the {@link MediaRoute2Info media route info}.
*
* @throws IllegalArgumentException if no features are added.
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index ba26df9..5e23551 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -17,6 +17,7 @@
package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
@@ -699,15 +700,48 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
- mImpl.transfer(controller.getRoutingSessionInfo(), route);
+ mImpl.transfer(
+ controller.getRoutingSessionInfo(),
+ route,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
+ }
+
+ /**
+ * Transfers the media of a routing controller to the given route.
+ *
+ * <p>This will be no-op for non-system media routers.
+ *
+ * @param controller a routing controller controlling media routing.
+ * @param route the route you want to transfer the media to.
+ * @param transferInitiatorUserHandle the user handle of the app that initiated the transfer
+ * request.
+ * @param transferInitiatorPackageName the package name of the app that initiated the transfer.
+ * This value is used with the user handle to populate {@link
+ * RoutingController#wasTransferRequestedBySelf()}.
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public void transfer(
+ @NonNull RoutingController controller,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
+ mImpl.transfer(
+ controller.getRoutingSessionInfo(),
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
void requestCreateController(
@NonNull RoutingController controller,
@NonNull MediaRoute2Info route,
- long managerRequestId) {
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
final int requestId = mNextRequestId.getAndIncrement();
@@ -736,7 +770,9 @@
managerRequestId,
controller.getRoutingSessionInfo(),
route,
- controllerHints);
+ controllerHints,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
Log.e(TAG, "createControllerForTransfer: "
+ "Failed to request for creating a controller.", ex);
@@ -1053,7 +1089,11 @@
}
void onRequestCreateControllerByManagerOnHandler(
- RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Log.i(
TAG,
TextUtils.formatSimple(
@@ -1070,7 +1110,8 @@
if (controller == null) {
return;
}
- requestCreateController(controller, route, managerRequestId);
+ requestCreateController(controller, route, managerRequestId, transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
private List<MediaRoute2Info> getSortedRoutes(
@@ -1469,6 +1510,21 @@
}
/**
+ * Returns whether the transfer was requested by the calling app (as determined by comparing
+ * {@link UserHandle} and package name).
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public boolean wasTransferRequestedBySelf() {
+ RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
+
+ UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
+ String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
+
+ return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle)
+ && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName);
+ }
+
+ /**
* Returns the current {@link RoutingSessionInfo} associated to this controller.
*/
@NonNull
@@ -1980,14 +2036,20 @@
@Override
public void requestCreateSessionByManager(
- long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
+ long managerRequestId,
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ UserHandle transferInitiatorUserHandle,
+ String transferInitiatorPackageName) {
mHandler.sendMessage(
obtainMessage(
MediaRouter2::onRequestCreateControllerByManagerOnHandler,
MediaRouter2.this,
oldSession,
route,
- managerRequestId));
+ managerRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName));
}
}
@@ -2027,7 +2089,11 @@
void stop();
- void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route);
+ void transfer(
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName);
List<RoutingController> getControllers();
@@ -2220,7 +2286,11 @@
List<RoutingSessionInfo> sessionInfos = getRoutingSessions();
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
- transfer(targetSession, route);
+ transfer(
+ targetSession,
+ route,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
}
@Override
@@ -2243,14 +2313,24 @@
*
* @param sessionInfo The {@link RoutingSessionInfo routing session} to transfer.
* @param route The {@link MediaRoute2Info route} to transfer to.
- * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info)
+ * @param transferInitiatorUserHandle The user handle of the app that initiated the
+ * transfer.
+ * @param transferInitiatorPackageName The package name if of the app that initiated the
+ * transfer.
+ * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info, UserHandle, String)
* @see #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)
*/
@Override
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
public void transfer(
- @NonNull RoutingSessionInfo sessionInfo, @NonNull MediaRoute2Info route) {
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
+ Objects.requireNonNull(transferInitiatorPackageName);
Log.v(
TAG,
@@ -2268,9 +2348,14 @@
}
if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- transferToRoute(sessionInfo, route);
+ transferToRoute(
+ sessionInfo,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} else {
- requestCreateSession(sessionInfo, route);
+ requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
}
@@ -2282,21 +2367,30 @@
* RoutingSessionInfo routing session's} {@link RoutingSessionInfo#getTransferableRoutes()
* transferable routes list}. Otherwise, the request will fail.
*
- * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request
- * an out-of-session transfer.
+ * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request an
+ * out-of-session transfer.
*
* @param session The {@link RoutingSessionInfo routing session} to transfer.
* @param route The {@link MediaRoute2Info route} to transfer to. Must be one of the {@link
* RoutingSessionInfo routing session's} {@link
* RoutingSessionInfo#getTransferableRoutes() transferable routes}.
*/
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
private void transferToRoute(
- @NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) {
+ @NonNull RoutingSessionInfo session,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
int requestId = createTransferRequest(session, route);
try {
mMediaRouterService.transferToRouteWithManager(
- mClient, requestId, session.getId(), route);
+ mClient,
+ requestId,
+ session.getId(),
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -2317,7 +2411,10 @@
* @param route The {@link MediaRoute2Info route} to transfer to.
*/
private void requestCreateSession(
- @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
this.onTransferFailed(oldSession, route);
@@ -2328,7 +2425,12 @@
try {
mMediaRouterService.requestCreateSessionWithManager(
- mClient, requestId, oldSession, route);
+ mClient,
+ requestId,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -3055,7 +3157,8 @@
return;
}
- requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
+ requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE,
+ android.os.Process.myUserHandle(), mContext.getPackageName());
}
@Override
@@ -3071,7 +3174,11 @@
* #transferTo(MediaRoute2Info)}.
*/
@Override
- public void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route) {
+ public void transfer(
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
// Do nothing.
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 830708c..06c0996 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -18,9 +18,11 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
@@ -28,6 +30,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -467,30 +470,42 @@
* <p>Same as {@link #transfer(RoutingSessionInfo, MediaRoute2Info)}, but resolves the routing
* session based on the provided package name.
*/
- public void transfer(@NonNull String packageName, @NonNull MediaRoute2Info route) {
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void transfer(
+ @NonNull String packageName,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle userHandle) {
Objects.requireNonNull(packageName, "packageName must not be null");
Objects.requireNonNull(route, "route must not be null");
List<RoutingSessionInfo> sessionInfos = getRoutingSessions(packageName);
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
- transfer(targetSession, route);
+ transfer(targetSession, route, userHandle, packageName);
}
/**
* Transfers a routing session to a media route.
+ *
* <p>{@link Callback#onTransferred} or {@link Callback#onTransferFailed} will be called
* depending on the result.
*
* @param sessionInfo the routing session info to transfer
* @param route the route transfer to
- *
+ * @param transferInitiatorUserHandle the user handle of an app initiated the transfer
+ * @param transferInitiatorPackageName the package name of an app initiated the transfer
* @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
* @see Callback#onTransferFailed(RoutingSessionInfo, MediaRoute2Info)
*/
- public void transfer(@NonNull RoutingSessionInfo sessionInfo,
- @NonNull MediaRoute2Info route) {
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void transfer(
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
+ Objects.requireNonNull(transferInitiatorPackageName);
Log.v(TAG, "Transferring routing session. session= " + sessionInfo + ", route=" + route);
@@ -503,9 +518,11 @@
}
if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- transferToRoute(sessionInfo, route);
+ transferToRoute(
+ sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName);
} else {
- requestCreateSession(sessionInfo, route);
+ requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
}
@@ -873,19 +890,30 @@
*
* @hide
*/
- private void transferToRoute(@NonNull RoutingSessionInfo session,
- @NonNull MediaRoute2Info route) {
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ private void transferToRoute(
+ @NonNull RoutingSessionInfo session,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
int requestId = createTransferRequest(session, route);
try {
mMediaRouterService.transferToRouteWithManager(
- mClient, requestId, session.getId(), route);
+ mClient,
+ requestId,
+ session.getId(),
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
- private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
+ private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiationPackageName) {
if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
notifyTransferFailed(oldSession, route);
@@ -896,7 +924,8 @@
try {
mMediaRouterService.requestCreateSessionWithManager(
- mClient, requestId, oldSession, route);
+ mClient, requestId, oldSession, route, transferInitiatorUserHandle,
+ transferInitiationPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index a77c943..d28c26d 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -16,18 +16,25 @@
package android.media;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -55,6 +62,33 @@
private static final String KEY_GROUP_ROUTE = "androidx.mediarouter.media.KEY_GROUP_ROUTE";
private static final String KEY_VOLUME_HANDLING = "volumeHandling";
+ /**
+ * Indicates that the transfer happened by the default logic without explicit system's or user's
+ * request.
+ *
+ * <p>For example, an automatically connected Bluetooth device will have this transfer reason.
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int TRANSFER_REASON_FALLBACK = 0;
+
+ /** Indicates that the transfer happened from within a privileged application. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1;
+
+ /** Indicates that the transfer happened from a non-privileged app. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int TRANSFER_REASON_APP = 2;
+
+ /**
+ * Indicates the transfer reason.
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @IntDef(value = {TRANSFER_REASON_FALLBACK, TRANSFER_REASON_SYSTEM_REQUEST, TRANSFER_REASON_APP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransferReason {}
+
@NonNull
final String mId;
@Nullable
@@ -82,6 +116,10 @@
final Bundle mControlHints;
final boolean mIsSystemSession;
+ @TransferReason final int mTransferReason;
+
+ @Nullable final UserHandle mTransferInitiatorUserHandle;
+ @Nullable final String mTransferInitiatorPackageName;
RoutingSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
@@ -116,6 +154,9 @@
volumeAdjustmentForRemoteGroupSessions);
mControlHints = updateVolumeHandlingInHints(builder.mControlHints, mVolumeHandling);
+ mTransferReason = builder.mTransferReason;
+ mTransferInitiatorUserHandle = builder.mTransferInitiatorUserHandle;
+ mTransferInitiatorPackageName = builder.mTransferInitiatorPackageName;
}
RoutingSessionInfo(@NonNull Parcel src) {
@@ -140,6 +181,9 @@
mControlHints = src.readBundle();
mIsSystemSession = src.readBoolean();
+ mTransferReason = src.readInt();
+ mTransferInitiatorUserHandle = src.readParcelable(null, android.os.UserHandle.class);
+ mTransferInitiatorPackageName = src.readString();
}
@Nullable
@@ -330,6 +374,27 @@
return mIsSystemSession;
}
+ /** Returns the transfer reason for this routing session. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @TransferReason
+ public int getTransferReason() {
+ return mTransferReason;
+ }
+
+ /** @hide */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @Nullable
+ public UserHandle getTransferInitiatorUserHandle() {
+ return mTransferInitiatorUserHandle;
+ }
+
+ /** @hide */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @Nullable
+ public String getTransferInitiatorPackageName() {
+ return mTransferInitiatorPackageName;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -351,6 +416,13 @@
dest.writeInt(mVolume);
dest.writeBundle(mControlHints);
dest.writeBoolean(mIsSystemSession);
+ dest.writeInt(mTransferReason);
+ if (mTransferInitiatorUserHandle != null) {
+ mTransferInitiatorUserHandle.writeToParcel(dest, /* flags= */ 0);
+ } else {
+ dest.writeParcelable(null, /* flags= */ 0);
+ }
+ dest.writeString(mTransferInitiatorPackageName);
}
/**
@@ -379,6 +451,9 @@
pw.println(indent + "mVolume=" + mVolume);
pw.println(indent + "mControlHints=" + mControlHints);
pw.println(indent + "mIsSystemSession=" + mIsSystemSession);
+ pw.println(indent + "mTransferReason=" + mTransferReason);
+ pw.println(indent + "mtransferInitiatorUserHandle=" + mTransferInitiatorUserHandle);
+ pw.println(indent + "mtransferInitiatorPackageName=" + mTransferInitiatorPackageName);
}
@Override
@@ -406,39 +481,69 @@
&& Objects.equals(mTransferableRoutes, other.mTransferableRoutes)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
- && (mVolume == other.mVolume);
+ && (mVolume == other.mVolume)
+ && (mTransferReason == other.mTransferReason)
+ && Objects.equals(mTransferInitiatorUserHandle, other.mTransferInitiatorUserHandle)
+ && Objects.equals(
+ mTransferInitiatorPackageName, other.mTransferInitiatorPackageName);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId,
- mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
- mVolumeMax, mVolumeHandling, mVolume);
+ return Objects.hash(
+ mId,
+ mName,
+ mOwnerPackageName,
+ mClientPackageName,
+ mProviderId,
+ mSelectedRoutes,
+ mSelectableRoutes,
+ mDeselectableRoutes,
+ mTransferableRoutes,
+ mVolumeMax,
+ mVolumeHandling,
+ mVolume,
+ mTransferReason,
+ mTransferInitiatorUserHandle,
+ mTransferInitiatorPackageName);
}
@Override
public String toString() {
- StringBuilder result = new StringBuilder()
- .append("RoutingSessionInfo{ ")
- .append("sessionId=").append(getId())
- .append(", name=").append(getName())
- .append(", clientPackageName=").append(getClientPackageName())
- .append(", selectedRoutes={")
- .append(String.join(",", getSelectedRoutes()))
- .append("}")
- .append(", selectableRoutes={")
- .append(String.join(",", getSelectableRoutes()))
- .append("}")
- .append(", deselectableRoutes={")
- .append(String.join(",", getDeselectableRoutes()))
- .append("}")
- .append(", transferableRoutes={")
- .append(String.join(",", getTransferableRoutes()))
- .append("}")
- .append(", volumeHandling=").append(getVolumeHandling())
- .append(", volumeMax=").append(getVolumeMax())
- .append(", volume=").append(getVolume())
- .append(" }");
+ StringBuilder result =
+ new StringBuilder()
+ .append("RoutingSessionInfo{ ")
+ .append("sessionId=")
+ .append(getId())
+ .append(", name=")
+ .append(getName())
+ .append(", clientPackageName=")
+ .append(getClientPackageName())
+ .append(", selectedRoutes={")
+ .append(String.join(",", getSelectedRoutes()))
+ .append("}")
+ .append(", selectableRoutes={")
+ .append(String.join(",", getSelectableRoutes()))
+ .append("}")
+ .append(", deselectableRoutes={")
+ .append(String.join(",", getDeselectableRoutes()))
+ .append("}")
+ .append(", transferableRoutes={")
+ .append(String.join(",", getTransferableRoutes()))
+ .append("}")
+ .append(", volumeHandling=")
+ .append(getVolumeHandling())
+ .append(", volumeMax=")
+ .append(getVolumeMax())
+ .append(", volume=")
+ .append(getVolume())
+ .append(", transferReason=")
+ .append(getTransferReason())
+ .append(", transferInitiatorUserHandle=")
+ .append(getTransferInitiatorUserHandle())
+ .append(", transferInitiatorPackageName=")
+ .append(getTransferInitiatorPackageName())
+ .append(" }");
return result.toString();
}
@@ -494,6 +599,9 @@
@Nullable
private Bundle mControlHints;
private boolean mIsSystemSession;
+ @TransferReason private int mTransferReason = TRANSFER_REASON_FALLBACK;
+ @Nullable private UserHandle mTransferInitiatorUserHandle;
+ @Nullable private String mTransferInitiatorPackageName;
/**
* Constructor for builder to create {@link RoutingSessionInfo}.
@@ -555,6 +663,9 @@
mControlHints = sessionInfo.mControlHints;
mIsSystemSession = sessionInfo.mIsSystemSession;
+ mTransferReason = sessionInfo.mTransferReason;
+ mTransferInitiatorUserHandle = sessionInfo.mTransferInitiatorUserHandle;
+ mTransferInitiatorPackageName = sessionInfo.mTransferInitiatorPackageName;
}
/**
@@ -784,6 +895,35 @@
}
/**
+ * Sets transfer reason for the current session.
+ *
+ * <p>By default the transfer reason is set to {@link
+ * RoutingSessionInfo#TRANSFER_REASON_FALLBACK}.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public Builder setTransferReason(@TransferReason int transferReason) {
+ mTransferReason = transferReason;
+ return this;
+ }
+
+ /**
+ * Sets the user handle and package name of the process that initiated the transfer.
+ *
+ * <p>By default the transfer initiation user handle and package name are set to {@code
+ * null}.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public Builder setTransferInitiator(
+ @Nullable UserHandle transferInitiatorUserHandle,
+ @Nullable String transferInitiatorPackageName) {
+ mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+ mTransferInitiatorPackageName = transferInitiatorPackageName;
+ return this;
+ }
+
+ /**
* Builds a routing session info.
*
* @throws IllegalArgumentException if no selected routes are added.
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 07f63e5..3da52cc 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -69,3 +69,11 @@
description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
bug: "314324170"
}
+
+flag {
+ name: "enable_built_in_speaker_route_suitability_statuses"
+ namespace: "media_solutions"
+ description: "Make MediaRoute2Info provide information about routes suitability for transfer."
+ bug: "279555229"
+}
+
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 8ed4bf2..c836df3 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -385,7 +385,9 @@
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
assertThat(routeToSelect).isNotNull();
- mManager.transfer(mPackageName, routeToSelect);
+ mManager.transfer(
+ mPackageName, routeToSelect,
+ android.os.Process.myUserHandle());
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(mManager.getRemoteSessions()).hasSize(1);
}
@@ -411,7 +413,9 @@
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
- mManager.transfer(mPackageName, routeToSelect);
+ mManager.transfer(
+ mPackageName, routeToSelect,
+ android.os.Process.myUserHandle());
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -450,7 +454,11 @@
.addFeature(FEATURE_REMOTE_PLAYBACK)
.build();
- mManager.transfer(mManager.getSystemRoutingSession(null), unknownRoute);
+ mManager.transfer(
+ mManager.getSystemRoutingSession(null),
+ unknownRoute,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
@@ -484,7 +492,11 @@
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
assertThat(mRouter2.getControllers()).hasSize(1);
- mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect);
+ mManager.transfer(
+ mManager.getRoutingSessions(mPackageName).get(0),
+ routeToSelect,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
assertThat(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(2);
@@ -516,7 +528,11 @@
}
});
awaitOnRouteChangedManager(
- () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID1)),
+ () ->
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID1),
+ android.os.Process.myUserHandle()),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -527,7 +543,11 @@
RoutingSessionInfo sessionInfo = sessions.get(1);
awaitOnRouteChangedManager(
- () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
+ () ->
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID5_TO_TRANSFER_TO),
+ android.os.Process.myUserHandle()),
ROUTE_ID5_TO_TRANSFER_TO,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
@@ -585,9 +605,11 @@
assertThat(route1).isNotNull();
assertThat(route2).isNotNull();
- mManager.transfer(mPackageName, route1);
+ mManager.transfer(
+ mPackageName, route1, android.os.Process.myUserHandle());
assertThat(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- mManager.transfer(mPackageName, route2);
+ mManager.transfer(
+ mPackageName, route2, android.os.Process.myUserHandle());
assertThat(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
// onTransferFailed/onSessionReleased should not be called.
@@ -634,7 +656,11 @@
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
RoutingSessionInfo targetSession = sessions.get(sessions.size() - 1);
- mManager.transfer(targetSession, routes.get(ROUTE_ID6_TO_BE_IGNORED));
+ mManager.transfer(
+ targetSession,
+ routes.get(ROUTE_ID6_TO_BE_IGNORED),
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
assertThat(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
@@ -705,7 +731,10 @@
}
});
- mManager.transfer(mPackageName, routes.get(ROUTE_ID1));
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID1),
+ android.os.Process.myUserHandle());
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -860,7 +889,8 @@
});
mRouter2.setOnGetControllerHintsListener(listener);
- mManager.transfer(mPackageName, route);
+ mManager.transfer(
+ mPackageName, route, android.os.Process.myUserHandle());
assertThat(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -905,7 +935,10 @@
}
});
- mManager.transfer(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT),
+ android.os.Process.myUserHandle());
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index 66bd6f5..d5cf1a35 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -26,6 +26,7 @@
import androidx.compose.material.icons.outlined.PowerOff
import androidx.compose.material.icons.outlined.Shield
import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -78,24 +79,19 @@
imageVector = Icons.Outlined.WarningAmber,
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Action", isMain = true) {},
- )
+ ),
+ tintColor = MaterialTheme.colorScheme.error,
+ containerColor = MaterialTheme.colorScheme.errorContainer,
)
)
}
@Composable
private fun SettingsCardWithoutIcon() {
- var isVisible by rememberSaveable { mutableStateOf(true) }
SettingsCard(
CardModel(
title = stringResource(R.string.sample_title),
text = stringResource(R.string.sample_text),
- isVisible = { isVisible },
- onDismiss = { isVisible = false },
- buttons = listOf(
- CardButton(text = "Action") {},
- ),
)
)
}
@@ -104,6 +100,7 @@
fun SampleSettingsCollapsibleCard() {
val context = LocalContext.current
var isVisible0 by rememberSaveable { mutableStateOf(true) }
+ var isVisible1 by rememberSaveable { mutableStateOf(true) }
val cards = remember {
mutableStateListOf(
CardModel(
@@ -114,16 +111,17 @@
onDismiss = { isVisible0 = false },
buttons = listOf(
CardButton(text = "Action") {},
- )
+ ),
),
CardModel(
title = context.getString(R.string.sample_title),
text = context.getString(R.string.sample_text),
imageVector = Icons.Outlined.Shield,
+ isVisible = { isVisible1 },
+ onDismiss = { isVisible1 = false },
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Main action", isMain = true) {},
- )
+ ),
)
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 993cb4a..c143390 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -37,7 +37,6 @@
val itemPaddingAround = 8.dp
val itemDividerHeight = 32.dp
- val iconSmall = 16.dp
val iconLarge = 48.dp
/** The size when app icon is displayed in list. */
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
index b18a1bc..b2a8b87 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -16,11 +16,11 @@
package com.android.settingslib.spa.widget.card
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
data class CardButton(
val text: String,
- val isMain: Boolean = false,
val onClick: () -> Unit,
)
@@ -38,4 +38,10 @@
val onDismiss: (() -> Unit)? = null,
val buttons: List<CardButton> = emptyList(),
+
+ /** If specified, this color will be used to tint the icon and the buttons. */
+ val tintColor: Color = Color.Unspecified,
+
+ /** If specified, this color will be used to tint the icon and the buttons. */
+ val containerColor: Color = Color.Unspecified,
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 7eec888..c7845fa 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -23,26 +23,26 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.WarningAmber
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -72,11 +72,14 @@
}
@Composable
-fun SettingsCardContent(content: @Composable ColumnScope.() -> Unit) {
+fun SettingsCardContent(
+ containerColor: Color = Color.Unspecified,
+ content: @Composable ColumnScope.() -> Unit,
+) {
Card(
shape = CornerExtraSmall,
colors = CardDefaults.cardColors(
- containerColor = SettingsTheme.colorScheme.surface,
+ containerColor = containerColor.takeOrElse { SettingsTheme.colorScheme.surface },
),
modifier = Modifier
.fillMaxWidth()
@@ -95,37 +98,43 @@
@Composable
internal fun SettingsCardImpl(model: CardModel) {
AnimatedVisibility(visible = model.isVisible()) {
- SettingsCardContent {
+ SettingsCardContent(containerColor = model.containerColor) {
Column(
- modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+ modifier = Modifier.padding(
+ horizontal = SettingsDimension.dialogItemPaddingHorizontal,
+ vertical = SettingsDimension.itemPaddingAround,
+ ),
verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
) {
- CardHeader(model.imageVector, model.onDismiss)
+ CardHeader(model.imageVector, model.tintColor, model.onDismiss)
SettingsTitle(model.title)
SettingsBody(model.text)
- Buttons(model.buttons)
+ Buttons(model.buttons, model.tintColor)
}
}
}
}
@Composable
-fun CardHeader(imageVector: ImageVector?, onDismiss: (() -> Unit)? = null) {
+fun CardHeader(imageVector: ImageVector?, iconColor: Color, onDismiss: (() -> Unit)? = null) {
+ if (imageVector != null || onDismiss != null) {
+ Spacer(Modifier.height(SettingsDimension.buttonPaddingVertical))
+ }
Row(Modifier.fillMaxWidth()) {
- CardIcon(imageVector)
+ CardIcon(imageVector, iconColor)
Spacer(modifier = Modifier.weight(1f))
DismissButton(onDismiss)
}
}
@Composable
-private fun CardIcon(imageVector: ImageVector?) {
+private fun CardIcon(imageVector: ImageVector?, color: Color) {
if (imageVector != null) {
Icon(
imageVector = imageVector,
contentDescription = null,
modifier = Modifier.size(SettingsDimension.itemIconSize),
- tint = MaterialTheme.colorScheme.primary,
+ tint = color.takeOrElse { MaterialTheme.colorScheme.primary },
)
}
}
@@ -146,52 +155,35 @@
contentDescription = stringResource(
androidx.compose.material3.R.string.m3c_snackbar_dismiss
),
- modifier = Modifier.size(SettingsDimension.iconSmall),
+ modifier = Modifier.padding(SettingsDimension.paddingSmall),
)
}
}
}
@Composable
-private fun Buttons(buttons: List<CardButton>) {
+private fun Buttons(buttons: List<CardButton>, color: Color) {
if (buttons.isNotEmpty()) {
Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = SettingsDimension.itemPaddingAround),
+ modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(
space = SettingsDimension.itemPaddingEnd,
alignment = Alignment.End,
),
) {
for (button in buttons) {
- Button(button)
+ Button(button, color)
}
}
+ } else {
+ Spacer(Modifier.height(SettingsDimension.itemPaddingAround))
}
}
@Composable
-private fun Button(button: CardButton) {
- if (button.isMain) {
- Button(
- onClick = button.onClick,
- colors = ButtonDefaults.buttonColors(
- containerColor = SettingsTheme.colorScheme.primaryContainer,
- ),
- ) {
- Text(
- text = button.text,
- color = SettingsTheme.colorScheme.onPrimaryContainer,
- )
- }
- } else {
- OutlinedButton(onClick = button.onClick) {
- Text(
- text = button.text,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
+private fun Button(button: CardButton, color: Color) {
+ TextButton(onClick = button.onClick) {
+ Text(text = button.text, color = color)
}
}
@@ -206,7 +198,6 @@
imageVector = Icons.Outlined.WarningAmber,
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Action", isMain = true) {},
)
)
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
index 6e36490..c34df65 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -141,7 +141,6 @@
imageVector = Icons.Outlined.Shield,
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Main action", isMain = true) {},
)
)
)
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index c51a9a0..8e5396f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -23,8 +23,6 @@
import static com.android.settingslib.Utils.getColorAttrDefaultColor;
import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -52,6 +50,8 @@
import android.view.MenuItem;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 3b8f665..70ece0f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -21,7 +21,6 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
@@ -35,6 +34,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 107d5f8..c2be571 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -3,7 +3,6 @@
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
import android.annotation.ColorInt;
-import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -23,10 +22,10 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
-import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.flags.Flags;
import android.location.LocationManager;
import android.media.AudioManager;
import android.net.NetworkCapabilities;
@@ -47,6 +46,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index dae48db..b0db16f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -17,7 +17,6 @@
package com.android.settingslib.applications;
import android.app.AppGlobals;
-import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -28,6 +27,8 @@
import android.os.RemoteException;
import android.util.IconDrawableFactory;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.widget.CandidateInfo;
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index fa056e2b..416b369 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -26,9 +26,9 @@
import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
index 1900575..cf4d6be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
@@ -21,8 +21,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
@@ -35,6 +33,9 @@
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
import java.lang.annotation.Retention;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
index c9512cd..8eaea0e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
@@ -151,7 +151,9 @@
private boolean removePreferredDeviceForStrategies(List<AudioProductStrategy> strategies) {
boolean status = true;
for (AudioProductStrategy strategy : strategies) {
- status &= mAudioManager.removePreferredDeviceForStrategy(strategy);
+ if (mAudioManager.getPreferredDeviceForStrategy(strategy) != null) {
+ status &= mAudioManager.removePreferredDeviceForStrategy(strategy);
+ }
}
return status;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 51164e8..57012aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -21,7 +21,6 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -31,6 +30,7 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 49ac0f8..de21c54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -19,7 +19,6 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -44,6 +43,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index 34008ac..34c60a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -20,7 +20,6 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -33,6 +32,7 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
index 3774b88..ab7a3db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
@@ -21,7 +21,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
-import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -31,6 +30,7 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
index be420ac..43c6c0d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -16,7 +16,6 @@
package com.android.settingslib.connectivity;
-import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +30,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
index 067afa4..f847154 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
@@ -14,7 +14,6 @@
package com.android.settingslib.core.instrumentation;
-import android.annotation.Nullable;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
@@ -24,6 +23,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.util.Map;
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
index 2bd0b27..e974e70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
@@ -22,13 +22,13 @@
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-import android.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.Menu;
import android.view.MenuItem;
+import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index bbb1ec6..0029e20 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -16,7 +16,6 @@
package com.android.settingslib.datetime;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.icu.text.TimeZoneFormat;
@@ -29,6 +28,7 @@
import android.util.Log;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.core.text.TextDirectionHeuristicsCompat;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 11fae24..f07daa3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -22,7 +22,6 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
-import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -45,6 +44,7 @@
import android.os.Build;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
index 0b3a519..f329023 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
@@ -16,11 +16,11 @@
package com.android.settingslib.enterprise;
-import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.DialogInterface;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index 714accc..7e3395b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -16,7 +16,6 @@
package com.android.settingslib.enterprise;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -24,6 +23,7 @@
import android.provider.Settings;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index 17c2b02..71d5809 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -16,7 +16,6 @@
package com.android.settingslib.graph;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -35,6 +34,8 @@
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
import com.android.settingslib.Utils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
index f01eb2a..65d53f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -16,7 +16,7 @@
package com.android.settingslib.graph;
-import android.annotation.NonNull;
+
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PorterDuff;
@@ -25,6 +25,7 @@
import android.graphics.drawable.LayerDrawable;
import android.view.Gravity;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index 4d0804e..9a19f93 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -16,8 +16,6 @@
import android.animation.ArgbEvaluator;
import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
@@ -36,6 +34,9 @@
import android.util.LayoutDirection;
import android.util.PathParser;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
import com.android.settingslib.Utils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
index 1712a6b..318e3dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
@@ -16,8 +16,6 @@
package com.android.settingslib.inputmethod;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
@@ -30,6 +28,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragment;
import androidx.preference.PreferenceScreen;
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
index 78ec58b..a2316435 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
@@ -16,8 +16,6 @@
package com.android.settingslib.inputmethod;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,6 +30,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index 4384400..5c0c979 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -17,7 +17,6 @@
package com.android.settingslib.inputmethod;
import android.annotation.AnyThread;
-import android.annotation.NonNull;
import android.annotation.UiThread;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,6 +25,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 52b51d7..ba40a50 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -43,8 +43,6 @@
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
@@ -59,6 +57,8 @@
import android.util.Log;
import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 0be2e0e..97bbf12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -16,8 +16,6 @@
package com.android.settingslib.media;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.media.MediaRoute2Info;
@@ -27,6 +25,9 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -80,14 +81,17 @@
@Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
- mRouterManager.transfer(mPackageName, route);
+ // TODO: b/279555229 - provide real user handle of a caller.
+ mRouterManager.transfer(mPackageName, route, android.os.Process.myUserHandle());
}
@Override
protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
if (info != null) {
- mRouterManager.transfer(info, device.mRouteInfo);
+ // TODO: b/279555229 - provide real user handle and package name of a caller.
+ mRouterManager.transfer(
+ info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName);
return true;
}
return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 80eeab5..0676ce5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -29,7 +29,6 @@
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import android.Manifest;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
@@ -40,6 +39,7 @@
import android.media.RouteListingPreference;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 5e9ac5a..bcfdebe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -16,7 +16,6 @@
package com.android.settingslib.net;
-import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -27,6 +26,7 @@
import android.util.Pair;
import android.util.Range;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.loader.content.AsyncTaskLoader;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 7fb959a..dfa5ed1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -16,7 +16,6 @@
package com.android.settingslib.notification;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
@@ -43,6 +42,8 @@
import android.widget.ScrollView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.PhoneWindow;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
index fbf8a2f..23b2cc2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
@@ -16,8 +16,6 @@
package com.android.settingslib.volume;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -42,6 +40,9 @@
import android.os.Message;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 303ee3c..cf45231 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -21,7 +21,6 @@
import android.annotation.IntDef;
import android.annotation.MainThread;
-import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -58,6 +57,7 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 98272cc..e508526 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -15,7 +15,6 @@
*/
package com.android.settingslib.wifi;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -32,6 +31,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
index 7ffae40..c0ca1252 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
@@ -18,12 +18,12 @@
import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
-import android.annotation.NonNull;
import android.content.Context;
import android.os.Bundle;
import android.os.UserManager;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.util.HashMap;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
index a0c2698..14967ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
@@ -22,7 +22,6 @@
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import android.annotation.AnyThread;
-import android.annotation.NonNull;
import android.annotation.TestApi;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -34,6 +33,7 @@
import android.util.Log;
import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
index 8b5ea30..c835244 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
@@ -18,8 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +45,7 @@
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -79,6 +82,8 @@
when(mAudioDeviceInfo.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(
new AudioDeviceInfo[]{mAudioDeviceInfo});
+ doReturn(Collections.emptyList()).when(mAudioManager).getPreferredDevicesForStrategy(
+ any(AudioProductStrategy.class));
when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
AudioManager.STREAM_MUSIC))
.thenReturn((new AudioAttributes.Builder()).build());
@@ -92,7 +97,10 @@
}
@Test
- public void setPreferredDeviceRoutingStrategies_valueAuto_callRemoveStrategy() {
+ public void setPreferredDeviceRoutingStrategies_hadValueThenValueAuto_callRemoveStrategy() {
+ when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(
+ mHearingDeviceAttribute);
+
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
HearingAidAudioRoutingConstants.RoutingValue.AUTO);
@@ -101,6 +109,17 @@
}
@Test
+ public void setPreferredDeviceRoutingStrategies_NoValueThenValueAuto_notCallRemoveStrategy() {
+ when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(null);
+
+ mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
+ mHearingDeviceAttribute,
+ HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+
+ verify(mAudioManager, never()).removePreferredDeviceForStrategy(mAudioStrategy);
+ }
+
+ @Test
public void setPreferredDeviceRoutingStrategies_valueHearingDevice_callSetStrategy() {
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
index 5aee8cd..194a0e2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -27,9 +27,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.RecyclerView;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
index 1d5f1b2..819d4b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
@@ -16,7 +16,7 @@
package com.android.settingslib.enterprise;
-import android.annotation.Nullable;
+import androidx.annotation.Nullable;
class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index c79440e..77c46f7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -37,6 +36,8 @@
import android.text.format.DateUtils;
import android.util.Range;
+import androidx.annotation.NonNull;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
index fae3aea..8448804 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
@@ -16,12 +16,13 @@
package com.android.settingslib.testutils.shadow;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.AttributionSource;
import android.content.Context;
import android.content.PermissionChecker;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index dac8142..fde378f 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -15,12 +15,13 @@
*/
package com.android.settingslib.testutils.shadow;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RoutingSessionInfo;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index b4c3850..98a2d9f 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -268,3 +268,17 @@
description: "Adds haptic feedback to the volume slider."
bug: "316953430"
}
+
+flag {
+ name: "screenshare_notification_hiding"
+ namespace: "systemui"
+ description: "Enable hiding of notifications during screenshare"
+ bug: "312784809"
+}
+
+flag {
+ name: "bluetooth_qs_tile_dialog_auto_on_toggle"
+ namespace: "systemui"
+ description: "Displays the auto on toggle in the bluetooth QS tile dialog"
+ bug: "316985153"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
new file mode 100644
index 0000000..efa8cc7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.blueprint
+
+import androidx.compose.ui.layout.HorizontalAlignmentLine
+import androidx.compose.ui.layout.VerticalAlignmentLine
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Encapsulates all blueprint alignment lines.
+ *
+ * These can be used to communicate alignment lines emitted by elements that the blueprint should
+ * consume and use to know how to constrain and/or place other elements in that blueprint.
+ *
+ * For more information, please see
+ * [the official documentation](https://developer.android.com/jetpack/compose/layouts/alignment-lines).
+ */
+object BlueprintAlignmentLines {
+
+ /**
+ * Encapsulates alignment lines produced by the lock icon element.
+ *
+ * Because the lock icon is also the same element as the under-display fingerprint sensor
+ * (UDFPS), blueprints should use its alignment lines to make sure that other elements on screen
+ * do not overlap with the lock icon.
+ */
+ object LockIcon {
+
+ /** The left edge of the lock icon. */
+ val Left =
+ VerticalAlignmentLine(
+ merger = { old, new ->
+ // When two left alignment line values are provided, choose the leftmost one:
+ min(old, new)
+ },
+ )
+
+ /** The top edge of the lock icon. */
+ val Top =
+ HorizontalAlignmentLine(
+ merger = { old, new ->
+ // When two top alignment line values are provided, choose the topmost one:
+ min(old, new)
+ },
+ )
+
+ /** The right edge of the lock icon. */
+ val Right =
+ VerticalAlignmentLine(
+ merger = { old, new ->
+ // When two right alignment line values are provided, choose the rightmost one:
+ max(old, new)
+ },
+ )
+
+ /** The bottom edge of the lock icon. */
+ val Bottom =
+ HorizontalAlignmentLine(
+ merger = { old, new ->
+ // When two bottom alignment line values are provided, choose the bottommost
+ // one:
+ max(old, new)
+ },
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index fc1df84..7314453 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -16,18 +16,13 @@
package com.android.systemui.keyguard.ui.composable.blueprint
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.width
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.ClockSection
@@ -62,47 +57,104 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- val context = LocalContext.current
- val lockIconBounds = lockSection.lockIconBounds(context)
val isUdfpsVisible = viewModel.isUdfpsVisible
- Box(
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ with(lockSection) { LockIcon() }
+
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ },
modifier = modifier,
- ) {
- Column(
- modifier = Modifier.fillMaxWidth().height { lockIconBounds.top },
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
+ ) { measurables, constraints ->
+ check(measurables.size == 5)
+ val (
+ aboveLockIconMeasurable,
+ lockIconMeasurable,
+ belowLockIconMeasurable,
+ startShortcutMeasurable,
+ endShortcutMeasurable,
+ ) = measurables
- with(lockSection) {
- LockIcon(
- modifier =
- Modifier.width { lockIconBounds.width() }
- .height { lockIconBounds.height() }
- .offset { IntOffset(lockIconBounds.left, lockIconBounds.top) }
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
)
- }
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
- Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
+ )
+ val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+ val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- with(bottomAreaSection) { BottomArea(modifier = Modifier.fillMaxWidth()) }
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ startShortcutPleaceable.place(
+ x = 0,
+ y = constraints.maxHeight - startShortcutPleaceable.height,
+ )
+ endShortcutPleaceable.place(
+ x = constraints.maxWidth - endShortcutPleaceable.width,
+ y = constraints.maxHeight - endShortcutPleaceable.height,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index fa913f1..4c119c7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -16,24 +16,13 @@
package com.android.systemui.keyguard.ui.composable.blueprint
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.offset
-import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.width
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.ClockSection
@@ -42,12 +31,10 @@
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
import javax.inject.Inject
-import kotlin.math.roundToInt
/**
* Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
@@ -70,96 +57,107 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- val context = LocalContext.current
- val lockIconBounds = lockSection.lockIconBounds(context)
val isUdfpsVisible = viewModel.isUdfpsVisible
- Box(
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ // Constrained to the left of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
+
+ with(lockSection) { LockIcon() }
+
+ // Constrained to the right of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
+
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
+ }
+ },
modifier = modifier,
- ) {
- Column(
- modifier = Modifier.fillMaxWidth().height { lockIconBounds.top },
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
- with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
- with(notificationSection) {
- Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
- }
- if (!isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
+ ) { measurables, constraints ->
+ check(measurables.size == 5)
+ val (
+ aboveLockIconMeasurable,
+ startSideShortcutMeasurable,
+ lockIconMeasurable,
+ endSideShortcutMeasurable,
+ belowLockIconMeasurable,
+ ) = measurables
- val shortcutSizePx =
- with(LocalDensity.current) { bottomAreaSection.shortcutSizeDp().toSize() }
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
- Row(
- verticalAlignment = Alignment.CenterVertically,
- modifier =
- Modifier.fillMaxWidth().offset {
- val rowTop =
- if (shortcutSizePx.height > lockIconBounds.height()) {
- (lockIconBounds.top -
- (shortcutSizePx.height + lockIconBounds.height()) / 2)
- .roundToInt()
- } else {
- lockIconBounds.top
- }
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
- IntOffset(0, rowTop)
- },
- ) {
- Spacer(Modifier.weight(1f))
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val startSideShortcutPlaceable = startSideShortcutMeasurable.measure(noMinConstraints)
+ val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = constraints.maxHeight - lockIconBounds.bottom)
+ )
- with(bottomAreaSection) { Shortcut(isStart = true) }
-
- Spacer(Modifier.weight(1f))
-
- with(lockSection) {
- LockIcon(
- modifier =
- Modifier.width { lockIconBounds.width() }
- .height { lockIconBounds.height() }
- )
- }
-
- Spacer(Modifier.weight(1f))
-
- with(bottomAreaSection) { Shortcut(isStart = false) }
-
- Spacer(Modifier.weight(1f))
- }
-
- Column(modifier = Modifier.fillMaxWidth().align(Alignment.BottomCenter)) {
- if (isUdfpsVisible) {
- with(ambientIndicationSection) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- horizontal =
- dimensionResource(
- R.dimen.keyguard_affordance_horizontal_offset
- )
- )
- .padding(
- bottom =
- dimensionResource(
- R.dimen.keyguard_affordance_vertical_offset
- )
- )
- .heightIn(min = shortcutSizeDp().height),
- )
- }
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ startSideShortcutPlaceable.placeRelative(
+ x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ endSideShortcutPlaceable.placeRelative(
+ x =
+ lockIconBounds.right + (constraints.maxWidth - lockIconBounds.right) / 2 -
+ endSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 53e4be3..db20f65 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -19,7 +19,6 @@
import android.view.View
import android.widget.ImageView
import androidx.annotation.IdRes
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -58,38 +57,17 @@
private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
) {
- @Composable
- fun SceneScope.BottomArea(
- modifier: Modifier = Modifier,
- ) {
- Row(
- modifier =
- modifier
- .padding(
- horizontal =
- dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
- )
- .padding(
- bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset)
- ),
- ) {
- Shortcut(
- isStart = true,
- )
-
- IndicationArea(
- modifier = Modifier.weight(1f),
- )
-
- Shortcut(
- isStart = false,
- )
- }
- }
-
+ /**
+ * Renders a single lockscreen shortcut.
+ *
+ * @param isStart Whether the shortcut goes on the left (in left-to-right locales).
+ * @param applyPadding Whether to apply padding around the shortcut, this is needed if the
+ * shortcut is placed along the edges of the display.
+ */
@Composable
fun SceneScope.Shortcut(
isStart: Boolean,
+ applyPadding: Boolean,
modifier: Modifier = Modifier,
) {
MovableElement(
@@ -103,6 +81,12 @@
falsingManager = falsingManager,
vibratorHelper = vibratorHelper,
indicationController = indicationController,
+ modifier =
+ if (applyPadding) {
+ Modifier.shortcutPadding()
+ } else {
+ Modifier
+ }
)
}
}
@@ -113,7 +97,7 @@
) {
MovableElement(
key = IndicationAreaElementKey,
- modifier = modifier,
+ modifier = modifier.shortcutPadding(),
) {
IndicationArea(
indicationAreaViewModel = indicationAreaViewModel,
@@ -218,6 +202,14 @@
modifier = modifier.fillMaxWidth(),
)
}
+
+ @Composable
+ private fun Modifier.shortcutPadding(): Modifier {
+ return this.padding(
+ horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
+ )
+ .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset))
+ }
}
private val StartButtonElementKey = ElementKey("StartButton")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 8bbe424b..d93863d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -17,8 +17,6 @@
package com.android.systemui.keyguard.ui.composable.section
import android.content.Context
-import android.graphics.Point
-import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.compose.foundation.background
@@ -28,11 +26,17 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.res.R
import javax.inject.Inject
@@ -49,8 +53,33 @@
key = LockIconElementKey,
modifier = modifier,
) {
+ val context = LocalContext.current
Box(
- modifier = Modifier.background(Color.Red),
+ modifier =
+ Modifier.background(Color.Red).layout { measurable, _ ->
+ val lockIconBounds = lockIconBounds(context)
+ val placeable =
+ measurable.measure(
+ Constraints.fixed(
+ width = lockIconBounds.width,
+ height = lockIconBounds.height,
+ )
+ )
+ layout(
+ width = placeable.width,
+ height = placeable.height,
+ alignmentLines =
+ mapOf(
+ BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left,
+ BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top,
+ BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right,
+ BlueprintAlignmentLines.LockIcon.Bottom to
+ lockIconBounds.bottom,
+ ),
+ ) {
+ placeable.place(0, 0)
+ }
+ },
) {
Text(
text = "TODO(b/316211368): Lock",
@@ -67,9 +96,9 @@
* On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are
* the same as the bounds of the sensor.
*/
- fun lockIconBounds(
+ private fun lockIconBounds(
context: Context,
- ): Rect {
+ ): IntRect {
val windowViewBounds = windowManager.currentWindowMetrics.bounds
var widthPx = windowViewBounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
@@ -85,36 +114,33 @@
val lockIconRadiusPx = (defaultDensity * 36).toInt()
val udfpsLocation = authController.udfpsLocation
- return if (authController.isUdfpsSupported && udfpsLocation != null) {
- centerLockIcon(udfpsLocation, authController.udfpsRadius)
- } else {
- val scaleFactor = authController.scaleFactor
- val bottomPaddingPx =
- context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
- val heightPx = windowViewBounds.bottom.toFloat()
+ val (center, radius) =
+ if (authController.isUdfpsSupported && udfpsLocation != null) {
+ Pair(
+ IntOffset(
+ x = udfpsLocation.x,
+ y = udfpsLocation.y,
+ ),
+ authController.udfpsRadius.toInt(),
+ )
+ } else {
+ val scaleFactor = authController.scaleFactor
+ val bottomPaddingPx =
+ context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ val heightPx = windowViewBounds.bottom.toFloat()
- centerLockIcon(
- Point(
- (widthPx / 2).toInt(),
- (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
- ),
- lockIconRadiusPx * scaleFactor
- )
- }
- }
+ Pair(
+ IntOffset(
+ x = (widthPx / 2).toInt(),
+ y =
+ (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor))
+ .toInt(),
+ ),
+ (lockIconRadiusPx * scaleFactor).toInt(),
+ )
+ }
- private fun centerLockIcon(
- center: Point,
- radius: Float,
- ): Rect {
- return Rect().apply {
- set(
- center.x - radius.toInt(),
- center.y - radius.toInt(),
- center.x + radius.toInt(),
- center.y + radius.toInt(),
- )
- }
+ return IntRect(center, radius)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index b9d6643..2bfa7d9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -33,6 +33,8 @@
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.MessageBuffer
import java.io.PrintWriter
@@ -51,12 +53,13 @@
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- var messageBuffer: MessageBuffer? = null
- set(value) {
- logger = if (value != null) Logger(value, TAG) else null
- }
-
- private var logger: Logger? = null
+ // To protect us from issues from this being null while the TextView constructor is running, we
+ // implement the get method and ensure a value is returned before initialization is complete.
+ private var logger = DEFAULT_LOGGER
+ get() = field ?: DEFAULT_LOGGER
+ var messageBuffer: MessageBuffer
+ get() = logger.buffer
+ set(value) { logger = Logger(value, TAG) }
private val time = Calendar.getInstance()
@@ -133,8 +136,8 @@
}
override fun onAttachedToWindow() {
+ logger.d("onAttachedToWindow")
super.onAttachedToWindow()
- logger?.d("onAttachedToWindow")
refreshFormat()
}
@@ -150,13 +153,13 @@
time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
contentDescription = DateFormat.format(descFormat, time)
val formattedText = DateFormat.format(format, time)
- logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
+ logger.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
// Setting text actually triggers a layout pass (because the text view is set to
// wrap_content width and TextView always relayouts for this). Avoid needless
// relayout if the text didn't actually change.
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
- logger?.d({ "refreshTime: done setting new time text to: $str1" }) {
+ logger.d({ "refreshTime: done setting new time text to: $str1" }) {
str1 = formattedText?.toString()
}
// Because the TextLayout may mutate under the hood as a result of the new text, we
@@ -165,21 +168,22 @@
// without being notified TextInterpolator being notified.
if (layout != null) {
textAnimator?.updateLayout(layout)
- logger?.d("refreshTime: done updating textAnimator layout")
+ logger.d("refreshTime: done updating textAnimator layout")
}
requestLayout()
- logger?.d("refreshTime: after requestLayout")
+ logger.d("refreshTime: after requestLayout")
}
}
fun onTimeZoneChanged(timeZone: TimeZone?) {
+ logger.d({ "onTimeZoneChanged($str1)" }) { str1 = timeZone?.toString() }
time.timeZone = timeZone
refreshFormat()
- logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() }
}
@SuppressLint("DrawAllocation")
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ logger.d("onMeasure")
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val animator = textAnimator
if (animator == null) {
@@ -189,10 +193,10 @@
} else {
animator.updateLayout(layout)
}
- logger?.d("onMeasure")
}
override fun onDraw(canvas: Canvas) {
+ logger.d({ "onDraw($str1)"}) { str1 = text.toString() }
// Use textAnimator to render text if animation is enabled.
// Otherwise default to using standard draw functions.
if (isAnimationEnabled) {
@@ -201,22 +205,23 @@
} else {
super.onDraw(canvas)
}
- logger?.d("onDraw")
}
override fun invalidate() {
+ @Suppress("UNNECESSARY_SAFE_CALL")
+ // logger won't be initialized when called by TextView's constructor
+ logger.d("invalidate")
super.invalidate()
- logger?.d("invalidate")
}
override fun onTextChanged(
- text: CharSequence,
- start: Int,
- lengthBefore: Int,
- lengthAfter: Int
+ text: CharSequence,
+ start: Int,
+ lengthBefore: Int,
+ lengthAfter: Int
) {
+ logger.d({ "onTextChanged($str1)" }) { str1 = text.toString() }
super.onTextChanged(text, start, lengthBefore, lengthAfter)
- logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() }
}
fun setLineSpacingScale(scale: Float) {
@@ -230,7 +235,7 @@
}
fun animateColorChange() {
- logger?.d("animateColorChange")
+ logger.d("animateColorChange")
setTextStyle(
weight = lockScreenWeight,
textSize = -1f,
@@ -252,7 +257,7 @@
}
fun animateAppearOnLockscreen() {
- logger?.d("animateAppearOnLockscreen")
+ logger.d("animateAppearOnLockscreen")
setTextStyle(
weight = dozingWeight,
textSize = -1f,
@@ -278,7 +283,7 @@
if (isAnimationEnabled && textAnimator == null) {
return
}
- logger?.d("animateFoldAppear")
+ logger.d("animateFoldAppear")
setTextStyle(
weight = lockScreenWeightInternal,
textSize = -1f,
@@ -305,7 +310,7 @@
// Skip charge animation if dozing animation is already playing.
return
}
- logger?.d("animateCharge")
+ logger.d("animateCharge")
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -329,7 +334,7 @@
}
fun animateDoze(isDozing: Boolean, animate: Boolean) {
- logger?.d("animateDoze")
+ logger.d("animateDoze")
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
@@ -448,7 +453,7 @@
isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
else -> DOUBLE_LINE_FORMAT_12_HOUR
}
- logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() }
+ logger.d({ "refreshFormat($str1)" }) { str1 = format?.toString() }
descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
refreshTime()
@@ -552,6 +557,8 @@
companion object {
private val TAG = AnimatableClockView::class.simpleName!!
+ private val DEFAULT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.WARNING), TAG)
+
const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600
private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index cdd074d..41bde52 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -21,20 +21,16 @@
import android.net.Uri
import android.os.UserHandle
import android.provider.Settings
-import android.util.Log
import androidx.annotation.OpenForTesting
-import com.android.systemui.log.LogMessageImpl
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.log.core.MessageInitializer
-import com.android.systemui.log.core.MessagePrinter
import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockProviderPlugin
@@ -77,32 +73,6 @@
return result ?: value
}
-private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
-
-private inline fun Logger?.tryLog(
- tag: String,
- level: LogLevel,
- messageInitializer: MessageInitializer,
- noinline messagePrinter: MessagePrinter,
- ex: Throwable? = null,
-) {
- if (this != null) {
- // Wrap messagePrinter to convert it from crossinline to noinline
- this.log(level, messagePrinter, ex, messageInitializer)
- } else {
- messageInitializer(TMP_MESSAGE)
- val msg = messagePrinter(TMP_MESSAGE)
- when (level) {
- LogLevel.VERBOSE -> Log.v(tag, msg, ex)
- LogLevel.DEBUG -> Log.d(tag, msg, ex)
- LogLevel.INFO -> Log.i(tag, msg, ex)
- LogLevel.WARNING -> Log.w(tag, msg, ex)
- LogLevel.ERROR -> Log.e(tag, msg, ex)
- LogLevel.WTF -> Log.wtf(tag, msg, ex)
- }
- }
-}
-
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
val context: Context,
@@ -114,12 +84,15 @@
val handleAllUsers: Boolean,
defaultClockProvider: ClockProvider,
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
- messageBuffer: MessageBuffer? = null,
+ val clockBuffers: ClockMessageBuffers? = null,
val keepAllLoaded: Boolean,
subTag: String,
var isTransitClockEnabled: Boolean = false,
) {
private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
+ private val logger: Logger =
+ Logger(clockBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.DEBUG), TAG)
+
interface ClockChangeListener {
// Called when the active clock changes
fun onCurrentClockChanged() {}
@@ -128,7 +101,6 @@
fun onAvailableClocksChanged() {}
}
- private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null
private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver =
@@ -157,21 +129,15 @@
val knownClocks = KNOWN_PLUGINS.get(manager.getPackage())
if (knownClocks == null) {
- logger.tryLog(
- TAG,
- LogLevel.WARNING,
- { str1 = manager.getPackage() },
- { "Loading unrecognized clock package: $str1" }
- )
+ logger.w({ "Loading unrecognized clock package: $str1" }) {
+ str1 = manager.getPackage()
+ }
return true
}
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- { str1 = manager.getPackage() },
- { "Skipping initial load of known clock package package: $str1" }
- )
+ logger.i({ "Skipping initial load of known clock package package: $str1" }) {
+ str1 = manager.getPackage()
+ }
var isCurrentClock = false
var isClockListChanged = false
@@ -185,19 +151,14 @@
}
if (manager != info.manager) {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- {
- str1 = id
- str2 = info.manager.toString()
- str3 = manager.toString()
- },
- {
- "Clock Id conflict on attach: " +
- "$str1 is double registered by $str2 and $str3"
- }
- )
+ logger.e({
+ "Clock Id conflict on attach: " +
+ "$str1 is double registered by $str2 and $str3"
+ }) {
+ str1 = id
+ str2 = info.manager.toString()
+ str3 = manager.toString()
+ }
continue
}
@@ -219,6 +180,8 @@
pluginContext: Context,
manager: PluginLifecycleManager<ClockProviderPlugin>
) {
+ plugin.initialize(clockBuffers)
+
var isClockListChanged = false
for (clock in plugin.getClocks()) {
val id = clock.clockId
@@ -233,19 +196,14 @@
}
if (manager != info.manager) {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- {
- str1 = id
- str2 = info.manager.toString()
- str3 = manager.toString()
- },
- {
- "Clock Id conflict on load: " +
- "$str1 is double registered by $str2 and $str3"
- }
- )
+ logger.e({
+ "Clock Id conflict on load: " +
+ "$str1 is double registered by $str2 and $str3"
+ }) {
+ str1 = id
+ str2 = info.manager.toString()
+ str3 = manager.toString()
+ }
manager.unloadPlugin()
continue
}
@@ -268,19 +226,14 @@
val id = clock.clockId
val info = availableClocks[id]
if (info?.manager != manager) {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- {
- str1 = id
- str2 = info?.manager.toString()
- str3 = manager.toString()
- },
- {
- "Clock Id conflict on unload: " +
- "$str1 is double registered by $str2 and $str3"
- }
- )
+ logger.e({
+ "Clock Id conflict on unload: " +
+ "$str1 is double registered by $str2 and $str3"
+ }) {
+ str1 = id
+ str2 = info?.manager.toString()
+ str3 = manager.toString()
+ }
continue
}
info.provider = null
@@ -350,7 +303,7 @@
ClockSettings.deserialize(json)
} catch (ex: Exception) {
- logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
+ logger.e("Failed to parse clock settings", ex)
null
}
settings = result
@@ -379,7 +332,7 @@
)
}
} catch (ex: Exception) {
- logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
+ logger.e("Failed to set clock settings", ex)
}
settings = value
}
@@ -451,7 +404,8 @@
}
init {
- // Register default clock designs
+ // Initialize & register default clock designs
+ defaultClockProvider.initialize(clockBuffers)
for (clock in defaultClockProvider.getClocks()) {
availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null)
}
@@ -514,12 +468,7 @@
fun verifyLoadedProviders() {
val shouldSchedule = isQueued.compareAndSet(false, true)
if (!shouldSchedule) {
- logger.tryLog(
- TAG,
- LogLevel.VERBOSE,
- {},
- { "verifyLoadedProviders: shouldSchedule=false" }
- )
+ logger.v("verifyLoadedProviders: shouldSchedule=false")
return
}
@@ -528,12 +477,7 @@
synchronized(availableClocks) {
isQueued.set(false)
if (keepAllLoaded) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: keepAllLoaded=true" }
- )
+ logger.i("verifyLoadedProviders: keepAllLoaded=true")
// Enforce that all plugins are loaded if requested
for ((_, info) in availableClocks) {
info.manager?.loadPlugin()
@@ -543,12 +487,7 @@
val currentClock = availableClocks[currentClockId]
if (currentClock == null) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: currentClock=null" }
- )
+ logger.i("verifyLoadedProviders: currentClock=null")
// Current Clock missing, load no plugins and use default
for ((_, info) in availableClocks) {
info.manager?.unloadPlugin()
@@ -556,12 +495,7 @@
return@launch
}
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: load currentClock" }
- )
+ logger.i("verifyLoadedProviders: load currentClock")
val currentManager = currentClock.manager
currentManager?.loadPlugin()
@@ -577,30 +511,26 @@
private fun onConnected(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Connected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
}
private fun onLoaded(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Loaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
if (isCurrent) {
triggerOnCurrentClockChanged()
@@ -609,16 +539,14 @@
private fun onUnloaded(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.WARNING else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Unloaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
if (isCurrent) {
triggerOnCurrentClockChanged()
@@ -627,16 +555,14 @@
private fun onDisconnected(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Disconnected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
}
fun getClocks(): List<ClockMetadata> {
@@ -676,23 +602,13 @@
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
- logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" })
+ logger.i({ "Rendering clock $str1" }) { str1 = clockId }
return clock
} else if (availableClocks.containsKey(clockId)) {
- logger.tryLog(
- TAG,
- LogLevel.WARNING,
- { str1 = clockId },
- { "Clock $str1 not loaded; using default" }
- )
+ logger.w({ "Clock $str1 not loaded; using default" }) { str1 = clockId }
verifyLoadedProviders()
} else {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- { str1 = clockId },
- { "Clock $str1 not found; using default" }
- )
+ logger.e({ "Clock $str1 not found; using default" }) { str1 = clockId }
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 01c03b1..99d3216 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.WeatherData
@@ -41,8 +42,6 @@
import java.util.Locale
import java.util.TimeZone
-private val TAG = DefaultClockController::class.simpleName
-
/**
* Controls the default clock visuals.
*
@@ -56,6 +55,7 @@
private val settings: ClockSettings?,
private val hasStepClockAnimation: Boolean = false,
private val migratedClocks: Boolean = false,
+ messageBuffers: ClockMessageBuffers? = null,
) : ClockController {
override val smallClock: DefaultClockFaceController
override val largeClock: LargeClockFaceController
@@ -83,13 +83,15 @@
DefaultClockFaceController(
layoutInflater.inflate(R.layout.clock_default_small, parent, false)
as AnimatableClockView,
- settings?.seedColor
+ settings?.seedColor,
+ messageBuffers?.smallClockMessageBuffer
)
largeClock =
LargeClockFaceController(
layoutInflater.inflate(R.layout.clock_default_large, parent, false)
as AnimatableClockView,
- settings?.seedColor
+ settings?.seedColor,
+ messageBuffers?.largeClockMessageBuffer
)
clocks = listOf(smallClock.view, largeClock.view)
@@ -110,6 +112,7 @@
open inner class DefaultClockFaceController(
override val view: AnimatableClockView,
var seedColor: Int?,
+ messageBuffer: MessageBuffer?,
) : ClockFaceController {
// MAGENTA is a placeholder, and will be assigned correctly in initialize
@@ -120,12 +123,6 @@
override val config = ClockFaceConfig()
override val layout = DefaultClockFaceLayout(view)
- override var messageBuffer: MessageBuffer?
- get() = view.messageBuffer
- set(value) {
- view.messageBuffer = value
- }
-
override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
internal set
@@ -134,6 +131,7 @@
currentColor = seedColor!!
}
view.setColors(DOZE_COLOR, currentColor)
+ messageBuffer?.let { view.messageBuffer = it }
}
override val events =
@@ -188,7 +186,8 @@
inner class LargeClockFaceController(
view: AnimatableClockView,
seedColor: Int?,
- ) : DefaultClockFaceController(view, seedColor) {
+ messageBuffer: MessageBuffer?,
+ ) : DefaultClockFaceController(view, seedColor, messageBuffer) {
override val layout = DefaultClockFaceLayout(view)
override val config =
ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index a219be5..20f87a0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -20,6 +20,7 @@
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockSettings
@@ -35,6 +36,12 @@
val hasStepClockAnimation: Boolean = false,
val migratedClocks: Boolean = false
) : ClockProvider {
+ private var messageBuffers: ClockMessageBuffers? = null
+
+ override fun initialize(buffers: ClockMessageBuffers?) {
+ messageBuffers = buffers
+ }
+
override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
override fun createClock(settings: ClockSettings): ClockController {
@@ -49,6 +56,7 @@
settings,
hasStepClockAnimation,
migratedClocks,
+ messageBuffers,
)
}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt
new file mode 100644
index 0000000..006b521
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+import android.util.Log
+import com.android.systemui.log.LogMessageImpl
+
+/**
+ * A simple implementation of [MessageBuffer] that forwards messages to [android.util.Log]
+ * immediately. This defeats the intention behind [LogBuffer] and should only be used when
+ * [LogBuffer]s are unavailable in a certain context.
+ */
+class LogcatOnlyMessageBuffer(
+ val targetLogLevel: LogLevel,
+) : MessageBuffer {
+ private val singleMessage = LogMessageImpl.Factory.create()
+ private var isObtained: Boolean = false
+
+ @Synchronized
+ override fun obtain(
+ tag: String,
+ level: LogLevel,
+ messagePrinter: MessagePrinter,
+ exception: Throwable?,
+ ): LogMessage {
+ if (isObtained) {
+ throw UnsupportedOperationException(
+ "Message has already been obtained. Call order is incorrect."
+ )
+ }
+
+ singleMessage.reset(tag, level, System.currentTimeMillis(), messagePrinter, exception)
+ isObtained = true
+ return singleMessage
+ }
+
+ @Synchronized
+ override fun commit(message: LogMessage) {
+ if (singleMessage != message) {
+ throw IllegalArgumentException("Message argument is not the expected message.")
+ }
+ if (!isObtained) {
+ throw UnsupportedOperationException(
+ "Message has not been obtained. Call order is incorrect."
+ )
+ }
+
+ if (message.level >= targetLogLevel) {
+ val strMessage = message.messagePrinter(message)
+ when (message.level) {
+ LogLevel.VERBOSE -> Log.v(message.tag, strMessage, message.exception)
+ LogLevel.DEBUG -> Log.d(message.tag, strMessage, message.exception)
+ LogLevel.INFO -> Log.i(message.tag, strMessage, message.exception)
+ LogLevel.WARNING -> Log.w(message.tag, strMessage, message.exception)
+ LogLevel.ERROR -> Log.e(message.tag, strMessage, message.exception)
+ LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception)
+ }
+ }
+
+ isObtained = false
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 1c5f221..4436be7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -41,16 +41,13 @@
/** Interface for building clocks and providing information about those clocks */
interface ClockProvider {
+ /** Initializes the clock provider with debug log buffers */
+ fun initialize(buffers: ClockMessageBuffers?)
+
/** Returns metadata for all clocks this provider knows about */
fun getClocks(): List<ClockMetadata>
/** Initializes and returns the target clock design */
- @Deprecated("Use overload with ClockSettings")
- fun createClock(id: ClockId): ClockController {
- return createClock(ClockSettings(id, null))
- }
-
- /** Initializes and returns the target clock design */
fun createClock(settings: ClockSettings): ClockController
/** A static thumbnail for rendering in some examples */
@@ -98,11 +95,20 @@
/** Triggers for various animations */
val animations: ClockAnimations
-
- /** Some clocks may log debug information */
- var messageBuffer: MessageBuffer?
}
+/** For clocks that want to report debug information */
+data class ClockMessageBuffers(
+ /** Message buffer for general infra */
+ val infraMessageBuffer: MessageBuffer,
+
+ /** Message buffer for small clock renering */
+ val smallClockMessageBuffer: MessageBuffer,
+
+ /** Message buffer for large clock rendering */
+ val largeClockMessageBuffer: MessageBuffer,
+)
+
/** Specifies layout information for the */
interface ClockFaceLayout {
/** All clock views to add to the root constraint layout before applying constraints. */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index d8c1e41..131eb6b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -367,7 +367,7 @@
/**
* Corner radius that should be used on windows in order to cover the display.
* These values are expressed in pixels because they should not respect display or font
- * scaling, this means that we don't have to reload them on config changes.
+ * scaling. The corner radius may change when folding/unfolding the device.
*/
public static float getWindowCornerRadius(Context context) {
return ScreenDecorationsUtils.getWindowCornerRadius(context);
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 76abad8..bcc2044 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -45,12 +45,11 @@
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.LogLevel.DEBUG
-import com.android.systemui.log.dagger.KeyguardLargeClockLog
-import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.WeatherData
@@ -91,117 +90,120 @@
private val context: Context,
@Main private val mainExecutor: DelayableExecutor,
@Background private val bgExecutor: Executor,
- @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?,
- @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?,
+ private val clockBuffers: ClockMessageBuffers,
private val featureFlags: FeatureFlags,
private val zenModeController: ZenModeController,
) {
+ var loggers = listOf(
+ clockBuffers.infraMessageBuffer,
+ clockBuffers.smallClockMessageBuffer,
+ clockBuffers.largeClockMessageBuffer
+ ).map { Logger(it, TAG) }
+
var clock: ClockController? = null
+ get() = field
set(value) {
- smallClockOnAttachStateChangeListener?.let {
- field?.smallClock?.view?.removeOnAttachStateChangeListener(it)
+ disconnectClock(field)
+ field = value
+ connectClock(value)
+ }
+
+ private fun disconnectClock(clock: ClockController?) {
+ if (clock == null) { return; }
+ smallClockOnAttachStateChangeListener?.let {
+ clock.smallClock.view.removeOnAttachStateChangeListener(it)
+ smallClockFrame?.viewTreeObserver
+ ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
+ }
+ largeClockOnAttachStateChangeListener?.let {
+ clock.largeClock.view.removeOnAttachStateChangeListener(it)
+ }
+ }
+
+ private fun connectClock(clock: ClockController?) {
+ if (clock == null) { return; }
+ val clockStr = clock.toString()
+ loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } }
+
+ clock.initialize(resources, dozeAmount, 0f)
+
+ if (!regionSamplingEnabled) {
+ updateColors()
+ } else {
+ smallRegionSampler = createRegionSampler(
+ clock.smallClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ ).apply { startRegionSampler() }
+
+ largeRegionSampler = createRegionSampler(
+ clock.largeClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ ).apply { startRegionSampler() }
+
+ updateColors()
+ }
+ updateFontSizes()
+ updateTimeListeners()
+
+ weatherData?.let {
+ if (WeatherData.DEBUG) {
+ Log.i(TAG, "Pushing cached weather data to new clock: $it")
+ }
+ clock.events.onWeatherDataChanged(it)
+ }
+ zenData?.let {
+ clock.events.onZenDataChanged(it)
+ }
+ alarmData?.let {
+ clock.events.onAlarmDataChanged(it)
+ }
+
+ smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
+ var pastVisibility: Int? = null
+ override fun onViewAttachedToWindow(view: View) {
+ clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ // Match the asing for view.parent's layout classes.
+ smallClockFrame = (view.parent as ViewGroup)?.also { frame ->
+ pastVisibility = frame.visibility
+ onGlobalLayoutListener = OnGlobalLayoutListener {
+ val currentVisibility = frame.visibility
+ if (pastVisibility != currentVisibility) {
+ pastVisibility = currentVisibility
+ // when small clock is visible,
+ // recalculate bounds and sample
+ if (currentVisibility == View.VISIBLE) {
+ smallRegionSampler?.stopRegionSampler()
+ smallRegionSampler?.startRegionSampler()
+ }
+ }
+ }
+ frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
+ }
+ }
+
+ override fun onViewDetachedFromWindow(p0: View) {
smallClockFrame?.viewTreeObserver
?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
}
- largeClockOnAttachStateChangeListener?.let {
- field?.largeClock?.view?.removeOnAttachStateChangeListener(it)
- }
-
- field = value
- if (value != null) {
- smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.smallClock.messageBuffer = smallLogBuffer
- largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.largeClock.messageBuffer = largeLogBuffer
-
- value.initialize(resources, dozeAmount, 0f)
-
- if (!regionSamplingEnabled) {
- updateColors()
- } else {
- clock?.let {
- smallRegionSampler = createRegionSampler(
- it.smallClock.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- )?.apply { startRegionSampler() }
-
- largeRegionSampler = createRegionSampler(
- it.largeClock.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- )?.apply { startRegionSampler() }
-
- updateColors()
- }
- }
- updateFontSizes()
- updateTimeListeners()
- weatherData?.let {
- if (WeatherData.DEBUG) {
- Log.i(TAG, "Pushing cached weather data to new clock: $it")
- }
- value.events.onWeatherDataChanged(it)
- }
- zenData?.let {
- value.events.onZenDataChanged(it)
- }
- alarmData?.let {
- value.events.onAlarmDataChanged(it)
- }
-
- smallClockOnAttachStateChangeListener =
- object : OnAttachStateChangeListener {
- var pastVisibility: Int? = null
- override fun onViewAttachedToWindow(view: View) {
- value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- // Match the asing for view.parent's layout classes.
- smallClockFrame = view.parent as ViewGroup
- smallClockFrame?.let { frame ->
- pastVisibility = frame.visibility
- onGlobalLayoutListener = OnGlobalLayoutListener {
- val currentVisibility = frame.visibility
- if (pastVisibility != currentVisibility) {
- pastVisibility = currentVisibility
- // when small clock is visible,
- // recalculate bounds and sample
- if (currentVisibility == View.VISIBLE) {
- smallRegionSampler?.stopRegionSampler()
- smallRegionSampler?.startRegionSampler()
- }
- }
- }
- frame.viewTreeObserver
- .addOnGlobalLayoutListener(onGlobalLayoutListener)
- }
- }
-
- override fun onViewDetachedFromWindow(p0: View) {
- smallClockFrame?.viewTreeObserver
- ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
- }
- }
- value.smallClock.view
- .addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
-
- largeClockOnAttachStateChangeListener =
- object : OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(p0: View) {
- value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- }
- override fun onViewDetachedFromWindow(p0: View) {
- }
- }
- value.largeClock.view
- .addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
- }
}
+ clock.smallClock.view.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+
+ largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(p0: View) {
+ clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+ override fun onViewDetachedFromWindow(p0: View) {}
+ }
+ clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+ }
@VisibleForTesting
var smallClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null
@@ -247,6 +249,7 @@
largeClock.events.onRegionDarknessChanged(isRegionDark)
}
}
+
protected open fun createRegionSampler(
sampledView: View,
mainExecutor: Executor?,
@@ -254,7 +257,7 @@
regionSamplingEnabled: Boolean,
isLockscreen: Boolean,
updateColors: () -> Unit
- ): RegionSampler? {
+ ): RegionSampler {
return RegionSampler(
sampledView,
mainExecutor,
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 661ce2c..878a5d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -28,9 +28,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.dagger.KeyguardClockLog;
import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
@@ -56,7 +55,7 @@
FeatureFlags featureFlags,
@Main Resources resources,
LayoutInflater layoutInflater,
- @KeyguardClockLog LogBuffer logBuffer) {
+ ClockMessageBuffers clockBuffers) {
ClockRegistry registry = new ClockRegistry(
context,
pluginManager,
@@ -72,7 +71,7 @@
featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION),
migrateClocksToBlueprint()),
context.getString(R.string.lockscreen_clock_id_fallback),
- logBuffer,
+ clockBuffers,
/* keepAllLoaded = */ false,
/* subTag = */ "System",
/* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK));
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 7d290c3..05fe0b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -32,8 +32,6 @@
import com.android.systemui.res.R
import kotlinx.coroutines.launch
-private val TAG = KeyguardClockViewBinder::class.simpleName
-
object KeyguardClockViewBinder {
@JvmStatic
fun bind(
@@ -74,12 +72,6 @@
applyConstraints(clockSection, keyguardRootView, true)
}
}
- launch {
- if (!migrateClocksToBlueprint()) return@launch
- viewModel.hasCustomWeatherDataDisplay.collect {
- applyConstraints(clockSection, keyguardRootView, true)
- }
- }
}
}
}
@@ -132,7 +124,7 @@
fun applyConstraints(
clockSection: ClockSection,
rootView: ConstraintLayout,
- animated: Boolean
+ animated: Boolean,
) {
val constraintSet = ConstraintSet().apply { clone(rootView) }
clockSection.applyConstraints(constraintSet)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 954d2cf..e36819b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -16,13 +16,19 @@
package com.android.systemui.keyguard.ui.binder
+import android.transition.TransitionManager
+import android.view.View
+import androidx.constraintlayout.helper.widget.Layer
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import kotlinx.coroutines.launch
object KeyguardSmartspaceViewBinder {
@JvmStatic
@@ -30,15 +36,63 @@
smartspaceSection: SmartspaceSection,
keyguardRootView: ConstraintLayout,
clockViewModel: KeyguardClockViewModel,
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) {
keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- clockViewModel.hasCustomWeatherDataDisplay.collect {
- val constraintSet = ConstraintSet().apply { clone(keyguardRootView) }
- smartspaceSection.applyConstraints(constraintSet)
- constraintSet.applyTo(keyguardRootView)
+ launch {
+ clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay
+ ->
+ if (hasCustomWeatherDataDisplay) {
+ removeDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel)
+ } else {
+ addDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel)
+ }
+ clockViewModel.burnInLayer?.updatePostLayout(keyguardRootView)
+ val constraintSet = ConstraintSet().apply { clone(keyguardRootView) }
+ smartspaceSection.applyConstraints(constraintSet)
+ TransitionManager.beginDelayedTransition(keyguardRootView)
+ constraintSet.applyTo(keyguardRootView)
+ }
}
}
}
}
+
+ private fun addDateWeatherToBurnInLayer(
+ constraintLayout: ConstraintLayout,
+ smartspaceViewModel: KeyguardSmartspaceViewModel
+ ) {
+ val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
+ burnInLayer.apply {
+ if (
+ smartspaceViewModel.isSmartspaceEnabled &&
+ smartspaceViewModel.isDateWeatherDecoupled
+ ) {
+ val dateView = constraintLayout.requireViewById<View>(smartspaceViewModel.dateId)
+ val weatherView =
+ constraintLayout.requireViewById<View>(smartspaceViewModel.weatherId)
+ addView(weatherView)
+ addView(dateView)
+ }
+ }
+ }
+
+ private fun removeDateWeatherToBurnInLayer(
+ constraintLayout: ConstraintLayout,
+ smartspaceViewModel: KeyguardSmartspaceViewModel
+ ) {
+ val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
+ burnInLayer.apply {
+ if (
+ smartspaceViewModel.isSmartspaceEnabled &&
+ smartspaceViewModel.isDateWeatherDecoupled
+ ) {
+ val dateView = smartspaceViewModel.dateView
+ val weatherView = smartspaceViewModel.weatherView
+ removeView(weatherView)
+ removeView(dateView)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index f890ae6..16539db 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -30,6 +30,8 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
import com.android.systemui.util.kotlin.getOrNull
@@ -59,6 +61,8 @@
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
+ smartspaceSection: SmartspaceSection,
+ clockSection: SplitShadeClockSection,
) : KeyguardBlueprint {
override val id: String = ID
@@ -73,8 +77,10 @@
splitShadeNotificationStackScrollLayoutSection,
splitShadeGuidelines,
aodNotificationIconsSection,
+ smartspaceSection,
aodBurnInSection,
communalTutorialIndicatorSection,
+ clockSection,
defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views.
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 8166b45..d89e1e4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -23,7 +23,6 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.Flags.migrateClocksToBlueprint
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -38,7 +37,6 @@
private val context: Context,
private val clockViewModel: KeyguardClockViewModel,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
- private val featureFlags: FeatureFlagsClassic,
) : KeyguardSection() {
lateinit var burnInLayer: Layer
@@ -59,6 +57,8 @@
}
}
if (migrateClocksToBlueprint()) {
+ // weather and date parts won't be added here, cause their visibility doesn't align
+ // with others in burnInLayer
addSmartspaceViews(constraintLayout)
}
constraintLayout.addView(burnInLayer)
@@ -89,14 +89,6 @@
val smartspaceView =
constraintLayout.requireViewById<View>(smartspaceViewModel.smartspaceViewId)
addView(smartspaceView)
- if (smartspaceViewModel.isDateWeatherDecoupled) {
- val dateView =
- constraintLayout.requireViewById<View>(smartspaceViewModel.dateId)
- val weatherView =
- constraintLayout.requireViewById<View>(smartspaceViewModel.weatherId)
- addView(weatherView)
- addView(dateView)
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 39a0547..b429ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -28,7 +28,6 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
@@ -49,7 +48,6 @@
constructor(
private val context: Context,
private val configurationState: ConfigurationState,
- private val featureFlags: FeatureFlagsClassic,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
@@ -119,14 +117,8 @@
}
constraintSet.apply {
if (migrateClocksToBlueprint()) {
- connect(
- nicId,
- TOP,
- smartspaceViewModel.smartspaceViewId,
- topAlignment,
- bottomMargin
- )
- setGoneMargin(nicId, topAlignment, bottomMargin)
+ connect(nicId, TOP, smartspaceViewModel.smartspaceViewId, BOTTOM, bottomMargin)
+ setGoneMargin(nicId, BOTTOM, bottomMargin)
} else {
connect(nicId, TOP, R.id.keyguard_status_view, topAlignment, bottomMargin)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 1df920a..656c75c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder
@@ -50,19 +50,21 @@
alpha: Float,
) = views.forEach { view -> this.setAlpha(view.id, alpha) }
-class ClockSection
+open class ClockSection
@Inject
constructor(
private val clockInteractor: KeyguardClockInteractor,
- private val keyguardClockViewModel: KeyguardClockViewModel,
- val smartspaceViewModel: KeyguardSmartspaceViewModel,
+ protected val keyguardClockViewModel: KeyguardClockViewModel,
+ private val smartspaceViewModel: KeyguardSmartspaceViewModel,
private val context: Context,
private val splitShadeStateController: SplitShadeStateController,
- private val featureFlags: FeatureFlagsClassic,
) : KeyguardSection() {
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!Flags.migrateClocksToBlueprint()) {
+ return
+ }
KeyguardClockViewBinder.bind(
this,
constraintLayout,
@@ -72,6 +74,9 @@
}
override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!Flags.migrateClocksToBlueprint()) {
+ return
+ }
clockInteractor.clock?.let { clock ->
constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
}
@@ -94,16 +99,6 @@
}
}
- var largeClockEndGuideline = PARENT_ID
-
- // Return if largeClockEndGuideline changes,
- // and use it to decide whether to refresh blueprint
- fun setClockShouldBeCentered(shouldBeCentered: Boolean): Boolean {
- val previousValue = largeClockEndGuideline
- largeClockEndGuideline = if (shouldBeCentered) PARENT_ID else R.id.split_shade_guideline
- return previousValue != largeClockEndGuideline
- }
-
private fun getTargetClockFace(clock: ClockController): ClockFaceLayout =
if (keyguardClockViewModel.useLargeClock) getLargeClockFace(clock)
else getSmallClockFace(clock)
@@ -113,10 +108,10 @@
private fun getLargeClockFace(clock: ClockController): ClockFaceLayout = clock.largeClock.layout
private fun getSmallClockFace(clock: ClockController): ClockFaceLayout = clock.smallClock.layout
- fun applyDefaultConstraints(constraints: ConstraintSet) {
+ open fun applyDefaultConstraints(constraints: ConstraintSet) {
constraints.apply {
connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START)
- connect(R.id.lockscreen_clock_view_large, END, largeClockEndGuideline, END)
+ connect(R.id.lockscreen_clock_view_large, END, PARENT_ID, END)
connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP)
var largeClockTopMargin =
context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 368b388..8cd51cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -36,7 +35,7 @@
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
-class SmartspaceSection
+open class SmartspaceSection
@Inject
constructor(
val keyguardClockViewModel: KeyguardClockViewModel,
@@ -45,9 +44,13 @@
val smartspaceController: LockscreenSmartspaceController,
val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
) : KeyguardSection() {
- private var smartspaceView: View? = null
- private var weatherView: View? = null
- private var dateView: View? = null
+ var smartspaceView by keyguardSmartspaceViewModel::smartspaceView
+ var weatherView by keyguardSmartspaceViewModel::weatherView
+ var dateView by keyguardSmartspaceViewModel::dateView
+
+ val smartspaceViewId = keyguardSmartspaceViewModel.smartspaceViewId
+ val weatherViewId = keyguardSmartspaceViewModel.weatherId
+ val dateViewId = keyguardSmartspaceViewModel.dateId
override fun addViews(constraintLayout: ConstraintLayout) {
if (!migrateClocksToBlueprint()) {
@@ -67,10 +70,21 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- KeyguardSmartspaceViewBinder.bind(this, constraintLayout, keyguardClockViewModel)
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+ KeyguardSmartspaceViewBinder.bind(
+ this,
+ constraintLayout,
+ keyguardClockViewModel,
+ keyguardSmartspaceViewModel,
+ )
}
override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
// Generally, weather should be next to dateView
// smartspace should be below date & weather views
constraintSet.apply {
@@ -130,7 +144,20 @@
}
}
}
- updateVisibility(constraintSet)
+ }
+ updateVisibility(constraintSet)
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+ listOf(smartspaceView, dateView, weatherView).forEach {
+ it?.let {
+ if (it.parent == constraintLayout) {
+ constraintLayout.removeView(it)
+ }
+ }
}
}
@@ -158,14 +185,4 @@
}
}
}
-
- override fun removeViews(constraintLayout: ConstraintLayout) {
- listOf(smartspaceView, dateView, weatherView).forEach {
- it?.let {
- if (it.parent == constraintLayout) {
- constraintLayout.removeView(it)
- }
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
new file mode 100644
index 0000000..1302bfa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
+import javax.inject.Inject
+
+class SplitShadeClockSection
+@Inject
+constructor(
+ clockInteractor: KeyguardClockInteractor,
+ keyguardClockViewModel: KeyguardClockViewModel,
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
+ context: Context,
+ splitShadeStateController: SplitShadeStateController,
+) :
+ ClockSection(
+ clockInteractor,
+ keyguardClockViewModel,
+ smartspaceViewModel,
+ context,
+ splitShadeStateController
+ ) {
+ override fun applyDefaultConstraints(constraints: ConstraintSet) {
+ super.applyDefaultConstraints(constraints)
+ val largeClockEndGuideline =
+ if (keyguardClockViewModel.clockShouldBeCentered.value) ConstraintSet.PARENT_ID
+ else R.id.split_shade_guideline
+ constraints.apply {
+ connect(
+ R.id.lockscreen_clock_view_large,
+ ConstraintSet.END,
+ largeClockEndGuideline,
+ ConstraintSet.END
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index b0b5c81..0f8e673 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,7 +23,6 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
@@ -72,25 +71,9 @@
return
}
constraintSet.apply {
- val bottomMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
-
- if (migrateClocksToBlueprint()) {
- connect(
- R.id.nssl_placeholder,
- TOP,
- smartspaceViewModel.smartspaceViewId,
- TOP,
- bottomMargin
- )
- setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin)
- } else {
- val splitShadeTopMargin =
- context.resources.getDimensionPixelSize(
- R.dimen.large_screen_shade_header_height
- )
- connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin)
- }
+ val splitShadeTopMargin =
+ context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+ connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin)
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 3aeff61..528a2ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -27,8 +27,8 @@
import com.android.systemui.plugins.clocks.ClockController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
@@ -79,10 +79,10 @@
?: false
)
- val clockShouldBeCentered: Flow<Boolean> =
+ val clockShouldBeCentered: StateFlow<Boolean> =
keyguardInteractor.clockShouldBeCentered.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = true
+ initialValue = false
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 4541458..26e7ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.util.Log
+import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
@@ -25,7 +26,7 @@
@SysUISingleton
class KeyguardSmartspaceViewModel
@Inject
-constructor(val context: Context, smartspaceController: LockscreenSmartspaceController) {
+constructor(val context: Context, val smartspaceController: LockscreenSmartspaceController) {
val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled()
val isWeatherEnabled: Boolean = smartspaceController.isWeatherEnabled()
val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled()
@@ -38,6 +39,10 @@
val weatherId: Int
get() = getId("weather_smartspace_view")
+ var smartspaceView: View? = null
+ var weatherView: View? = null
+ var dateView: View? = null
+
private fun getId(name: String): Int {
return context.resources.getIdentifier(name, "id", context.packageName).also {
if (it == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index d8bb3e6..0d5ba64 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -26,6 +26,7 @@
import com.android.systemui.log.echo.LogcatEchoTrackerProd;
import com.android.systemui.log.table.TableLogBuffer;
import com.android.systemui.log.table.TableLogBufferFactory;
+import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.pipeline.shared.TileSpec;
@@ -417,6 +418,18 @@
}
/**
+ * Provides a {@link ClockMessageBuffers} which contains the keyguard clock message buffers.
+ */
+ @Provides
+ public static ClockMessageBuffers provideKeyguardClockMessageBuffers(
+ @KeyguardClockLog LogBuffer infraClockLog,
+ @KeyguardSmallClockLog LogBuffer smallClockLog,
+ @KeyguardLargeClockLog LogBuffer largeClockLog
+ ) {
+ return new ClockMessageBuffers(infraClockLog, smallClockLog, largeClockLog);
+ }
+
+ /**
* Provides a {@link LogBuffer} for use by {@link com.android.keyguard.KeyguardUpdateMonitor}.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
index 2345667..83b6f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -19,16 +19,21 @@
import android.service.quicksettings.IQSTileService;
import android.util.Log;
+import androidx.annotation.NonNull;
+
public class QSTileServiceWrapper {
private static final String TAG = "IQSTileServiceWrapper";
+ @NonNull
private final IQSTileService mService;
- public QSTileServiceWrapper(IQSTileService service) {
+ public QSTileServiceWrapper(@NonNull IQSTileService service) {
mService = service;
}
+ // This can never be null, as it's the constructor parameter and it's final
+ @NonNull
public IBinder asBinder() {
return mService.asBinder();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index e08eb37..880289e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -40,6 +40,7 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -54,8 +55,10 @@
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
/**
* Manages the lifecycle of a TileService.
@@ -101,8 +104,8 @@
private final ActivityManager mActivityManager;
private Set<Integer> mQueuedMessages = new ArraySet<>();
- @Nullable
- private volatile QSTileServiceWrapper mWrapper;
+ @NonNull
+ private volatile Optional<QSTileServiceWrapper> mOptionalWrapper = Optional.empty();
private boolean mListening;
private IBinder mClickBinder;
@@ -222,6 +225,7 @@
// Only try a new binding if we are not currently bound.
mIsBound.compareAndSet(false, bindServices());
if (!mIsBound.get()) {
+ Log.d(TAG, "Failed to bind to service");
mContext.unbindService(this);
}
} catch (SecurityException e) {
@@ -281,7 +285,7 @@
service.linkToDeath(this, 0);
} catch (RemoteException e) {
}
- mWrapper = wrapper;
+ mOptionalWrapper = Optional.of(wrapper);
handlePendingMessages();
}
@@ -368,6 +372,10 @@
* are supposed to be bound, we will try to bind after some amount of time.
*/
private void handleDeath() {
+ if (!mIsBound.get()) {
+ // If we are already not bound, don't do anything else.
+ return;
+ }
mExecutor.execute(() -> {
if (!mIsBound.get()) {
// If we are already not bound, don't do anything else.
@@ -522,7 +530,7 @@
@Override
public void onTileAdded() {
if (mDebug) Log.d(TAG, "onTileAdded " + getComponent());
- if (mWrapper == null || !mWrapper.onTileAdded()) {
+ if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileAdded)) {
queueMessage(MSG_ON_ADDED);
handleDeath();
}
@@ -531,7 +539,7 @@
@Override
public void onTileRemoved() {
if (mDebug) Log.d(TAG, "onTileRemoved " + getComponent());
- if (mWrapper == null || !mWrapper.onTileRemoved()) {
+ if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileRemoved)) {
queueMessage(MSG_ON_REMOVED);
handleDeath();
}
@@ -541,7 +549,7 @@
public void onStartListening() {
if (mDebug) Log.d(TAG, "onStartListening " + getComponent());
mListening = true;
- if (mWrapper != null && !mWrapper.onStartListening()) {
+ if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStartListening)) {
handleDeath();
}
}
@@ -550,7 +558,7 @@
public void onStopListening() {
if (mDebug) Log.d(TAG, "onStopListening " + getComponent());
mListening = false;
- if (mWrapper != null && !mWrapper.onStopListening()) {
+ if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStopListening)) {
handleDeath();
}
}
@@ -558,7 +566,7 @@
@Override
public void onClick(IBinder iBinder) {
if (mDebug) Log.d(TAG, "onClick " + iBinder + " " + getComponent() + " " + mUser);
- if (mWrapper == null || !mWrapper.onClick(iBinder)) {
+ if (isNullOrFailedAction(mOptionalWrapper, (wrapper) -> wrapper.onClick(iBinder))) {
mClickBinder = iBinder;
queueMessage(MSG_ON_CLICK);
handleDeath();
@@ -568,7 +576,7 @@
@Override
public void onUnlockComplete() {
if (mDebug) Log.d(TAG, "onUnlockComplete " + getComponent());
- if (mWrapper == null || !mWrapper.onUnlockComplete()) {
+ if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onUnlockComplete)) {
queueMessage(MSG_ON_UNLOCK_COMPLETE);
handleDeath();
}
@@ -577,7 +585,7 @@
@Nullable
@Override
public IBinder asBinder() {
- return mWrapper != null ? mWrapper.asBinder() : null;
+ return mOptionalWrapper.map(QSTileServiceWrapper::asBinder).orElse(null);
}
@Override
@@ -591,18 +599,42 @@
}
private void freeWrapper() {
- if (mWrapper != null) {
+ if (mOptionalWrapper.isPresent()) {
try {
- mWrapper.asBinder().unlinkToDeath(this, 0);
+ mOptionalWrapper.ifPresent(
+ (wrapper) -> wrapper.asBinder().unlinkToDeath(this, 0)
+ );
} catch (NoSuchElementException e) {
Log.w(TAG, "Trying to unlink not linked recipient for component"
+ mIntent.getComponent().flattenToShortString());
}
- mWrapper = null;
+ mOptionalWrapper = Optional.empty();
}
}
public interface TileChangeListener {
void onTileChanged(ComponentName tile);
}
+
+ /**
+ * Returns true if the Optional is empty OR performing the action on the content of the Optional
+ * (when not empty) fails.
+ */
+ private static boolean isNullOrFailedAction(
+ Optional<QSTileServiceWrapper> optionalWrapper,
+ Predicate<QSTileServiceWrapper> action
+ ) {
+ return !optionalWrapper.map(action::test).orElse(false);
+ }
+
+ /**
+ * Returns true if the Optional is not empty AND performing the action on the content of
+ * the Optional fails.
+ */
+ private static boolean isNotNullAndFailedAction(
+ Optional<QSTileServiceWrapper> optionalWrapper,
+ Predicate<QSTileServiceWrapper> action
+ ) {
+ return !optionalWrapper.map(action::test).orElse(true);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 66da8bd..216d716 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -42,9 +42,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -63,8 +61,7 @@
private val keyguardDismissUtil: KeyguardDismissUtil,
private val keyguardStateController: KeyguardStateController,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val sysuiDialogFactory: SystemUIDialog.Factory,
- private val userContextProvider: UserContextProvider,
+ private val delegateFactory: RecordIssueDialogDelegate.Factory,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -102,7 +99,8 @@
private fun showPrompt(view: View?) {
val dialog: AlertDialog =
- RecordIssueDialogDelegate(sysuiDialogFactory, userContextProvider) {
+ delegateFactory
+ .create {
isRecording = true
refreshState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 5bf44fa..e051df4 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -24,27 +24,63 @@
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
+import android.os.UserHandle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.WindowManager
import android.widget.Button
import android.widget.PopupMenu
import android.widget.Switch
+import androidx.annotation.AnyThread
+import androidx.annotation.MainThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.SessionCreationSource
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog
+import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
import com.android.systemui.screenrecord.RecordingService
import com.android.systemui.screenrecord.ScreenRecordingAudioSource
import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
-class RecordIssueDialogDelegate(
+class RecordIssueDialogDelegate
+@AssistedInject
+constructor(
private val factory: SystemUIDialog.Factory,
private val userContextProvider: UserContextProvider,
- private val onStarted: Runnable
+ private val userTracker: UserTracker,
+ private val flags: FeatureFlagsClassic,
+ @Background private val bgExecutor: Executor,
+ @Main private val mainExecutor: Executor,
+ private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ private val userFileManager: UserFileManager,
+ @Assisted private val onStarted: Runnable,
) : SystemUIDialog.Delegate {
+ /** To inject dependencies and allow for easier testing */
+ @AssistedFactory
+ interface Factory {
+ /** Create a dialog object */
+ fun create(onStarted: Runnable): RecordIssueDialogDelegate
+ }
+
@SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
private lateinit var issueTypeButton: Button
+ @MainThread
override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
dialog.apply {
setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null))
@@ -63,17 +99,64 @@
override fun createDialog(): SystemUIDialog = factory.create(this)
+ @MainThread
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
dialog.apply {
window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
window?.setGravity(Gravity.CENTER)
screenRecordSwitch = requireViewById(R.id.screenrecord_switch)
+ screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled ->
+ onScreenRecordSwitchClicked(context, isEnabled)
+ }
issueTypeButton = requireViewById(R.id.issue_type_button)
issueTypeButton.setOnClickListener { onIssueTypeClicked(context) }
}
}
+ @AnyThread
+ private fun onScreenRecordSwitchClicked(context: Context, isEnabled: Boolean) {
+ if (!isEnabled) return
+
+ bgExecutor.execute {
+ if (
+ flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
+ devicePolicyResolver
+ .get()
+ .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
+ ) {
+ mainExecutor.execute {
+ ScreenCaptureDisabledDialog(context).show()
+ screenRecordSwitch.isChecked = false
+ }
+ return@execute
+ }
+
+ mediaProjectionMetricsLogger.notifyProjectionInitiated(
+ userTracker.userId,
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+ )
+
+ if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
+ val prefs =
+ userFileManager.getSharedPreferences(
+ RecordIssueTile.TILE_SPEC,
+ Context.MODE_PRIVATE,
+ userTracker.userId
+ )
+ if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) {
+ mainExecutor.execute {
+ ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply {
+ setOnCancelListener { screenRecordSwitch.isChecked = false }
+ show()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @MainThread
private fun onIssueTypeClicked(context: Context) {
val selectedCategory = issueTypeButton.text.toString()
val popupMenu = PopupMenu(context, issueTypeButton)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
new file mode 100644
index 0000000..de6d3f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
+
+class ScreenCapturePermissionDialogDelegate(
+ private val dialogFactory: SystemUIDialog.Factory,
+ private val sharedPreferences: SharedPreferences,
+) : SystemUIDialog.Delegate {
+
+ override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ dialog.apply {
+ setIcon(R.drawable.ic_screenrecord)
+ setTitle(R.string.screenrecord_permission_dialog_title)
+ setMessage(R.string.screenrecord_permission_dialog_warning_entire_screen)
+ setNegativeButton(R.string.slice_permission_deny) { _, _ -> cancel() }
+ setPositiveButton(R.string.slice_permission_allow) { _, _ ->
+ sharedPreferences.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, true).apply()
+ dismiss()
+ }
+ window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+ window?.setGravity(Gravity.CENTER)
+ }
+ }
+
+ override fun createDialog(): SystemUIDialog = dialogFactory.create(this)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 878e6fa..17eb3c8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1720,9 +1720,12 @@
}
// To prevent the weather clock from overlapping with the notification shelf on AOD, we use
// the small clock here
- if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
- && hasVisibleNotifications() && isOnAod()) {
- return SMALL;
+ // With migrateClocksToBlueprint, weather clock will have behaviors similar to other clocks
+ if (!migrateClocksToBlueprint()) {
+ if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
+ && hasVisibleNotifications() && isOnAod()) {
+ return SMALL;
+ }
}
return LARGE;
}
@@ -1742,8 +1745,9 @@
} else {
layout = mNotificationContainerParent;
}
+
if (migrateClocksToBlueprint()) {
- mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered);
+ mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered);
} else {
mKeyguardStatusViewController.updateAlignment(
layout, mSplitShadeEnabled, shouldBeCentered, animate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2740cc6..11456ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -453,11 +453,11 @@
public void initNotificationIconArea() {
ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area);
if (NotificationIconContainerRefactor.isEnabled()) {
- mNotificationIconAreaInner =
- LayoutInflater.from(getContext())
- .inflate(R.layout.notification_icon_area, notificationIconArea, true);
+ LayoutInflater.from(getContext())
+ .inflate(R.layout.notification_icon_area, notificationIconArea, true);
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
+ mNotificationIconAreaInner = notificationIcons;
mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
} else {
mNotificationIconAreaInner =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6f58bc2..e6637e6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -30,13 +30,15 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -94,9 +96,9 @@
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
private lateinit var repository: FakeKeyguardRepository
- @Mock private lateinit var smallLogBuffer: LogBuffer
- @Mock private lateinit var largeLogBuffer: LogBuffer
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
+ private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
private lateinit var underTest: ClockEventController
@Mock private lateinit var zenModeController: ZenModeController
@@ -140,8 +142,7 @@
context,
mainExecutor,
bgExecutor,
- smallLogBuffer,
- largeLogBuffer,
+ clockBuffers,
withDeps.featureFlags,
zenModeController
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index e89b61f..dc0d9c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -20,7 +20,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
@@ -43,7 +42,6 @@
@Mock private lateinit var keyguardClockViewModel: KeyguardClockViewModel
@Mock private lateinit var smartspaceViewModel: KeyguardSmartspaceViewModel
@Mock private lateinit var splitShadeStateController: SplitShadeStateController
- private var featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
private lateinit var underTest: ClockSection
@@ -88,7 +86,6 @@
smartspaceViewModel,
mContext,
splitShadeStateController,
- featureFlags
)
}
@@ -147,24 +144,6 @@
assetSmallClockTop(cs, expectedSmallClockTopMargin)
}
- @Test
- fun testLargeClockShouldBeCentered() {
- underTest.setClockShouldBeCentered(true)
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
- val constraint = cs.getConstraint(R.id.lockscreen_clock_view_large)
- assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
- }
-
- @Test
- fun testLargeClockShouldNotBeCentered() {
- underTest.setClockShouldBeCentered(false)
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
- val constraint = cs.getConstraint(R.id.lockscreen_clock_view_large)
- assertThat(constraint.layout.endToEnd).isEqualTo(R.id.split_shade_guideline)
- }
-
private fun setLargeClock(useLargeClock: Boolean) {
whenever(keyguardClockViewModel.useLargeClock).thenReturn(useLargeClock)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index bff27f6..740110b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -69,14 +69,13 @@
keyguardUnlockAnimationController,
)
constraintLayout = ConstraintLayout(mContext)
- whenever(lockscreenSmartspaceController.buildAndConnectView(constraintLayout))
- .thenReturn(smartspaceView)
- whenever(lockscreenSmartspaceController.buildAndConnectWeatherView(constraintLayout))
- .thenReturn(weatherView)
- whenever(lockscreenSmartspaceController.buildAndConnectDateView(constraintLayout))
- .thenReturn(dateView)
+ whenever(keyguardSmartspaceViewModel.smartspaceView).thenReturn(smartspaceView)
+ whenever(keyguardSmartspaceViewModel.weatherView).thenReturn(weatherView)
+ whenever(keyguardSmartspaceViewModel.dateView).thenReturn(dateView)
whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay)
.thenReturn(hasCustomWeatherDataDisplay)
+ whenever(keyguardSmartspaceViewModel.smartspaceController)
+ .thenReturn(lockscreenSmartspaceController)
constraintSet = ConstraintSet()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 459a74c..ee1be10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -24,7 +24,6 @@
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
import com.android.systemui.SysuiTestCase
-import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index 9b61447..c7479fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -30,8 +30,8 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -65,9 +65,9 @@
@Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var dialogLauncherAnimator: DialogLaunchAnimator
- @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+ @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
+ @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
@Mock private lateinit var dialog: SystemUIDialog
- @Mock private lateinit var userContextProvider: UserContextProvider
private lateinit var testableLooper: TestableLooper
private lateinit var tile: RecordIssueTile
@@ -76,7 +76,8 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(host.context).thenReturn(mContext)
- whenever(dialogFactory.create(any())).thenReturn(dialog)
+ whenever(delegateFactory.create(any())).thenReturn(dialogDelegate)
+ whenever(dialogDelegate.createDialog()).thenReturn(dialog)
testableLooper = TestableLooper.get(this)
tile =
@@ -93,8 +94,7 @@
keyguardDismissUtil,
keyguardStateController,
dialogLauncherAnimator,
- dialogFactory,
- userContextProvider,
+ delegateFactory,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index c5d3524..7ce51ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -17,6 +17,9 @@
package com.android.systemui.recordissue
import android.app.Dialog
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.widget.Button
@@ -25,48 +28,107 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.SessionCreationSource
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
import com.android.systemui.model.SysUiState
+import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class RecordIssueDialogDelegateTest : SysuiTestCase() {
+ @Mock private lateinit var flags: FeatureFlagsClassic
+ @Mock private lateinit var devicePolicyResolver: ScreenCaptureDevicePolicyResolver
+ @Mock private lateinit var dprLazy: dagger.Lazy<ScreenCaptureDevicePolicyResolver>
+ @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
+ @Mock private lateinit var userContextProvider: UserContextProvider
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userFileManager: UserFileManager
+ @Mock private lateinit var sharedPreferences: SharedPreferences
+
+ @Mock private lateinit var sysuiState: SysUiState
+ @Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var bgExecutor: Executor
+ @Mock private lateinit var mainExecutor: Executor
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
private lateinit var dialog: SystemUIDialog
+ private lateinit var factory: SystemUIDialog.Factory
private lateinit var latch: CountDownLatch
@Before
fun setup() {
- val dialogFactory =
- SystemUIDialog.Factory(
- context,
- mock<FeatureFlags>(),
- mock<SystemUIDialogManager>(),
- mock<SysUiState>().apply {
- whenever(setFlag(anyInt(), anyBoolean())).thenReturn(this)
- },
- mock<BroadcastDispatcher>(),
- mock<DialogLaunchAnimator>()
+ MockitoAnnotations.initMocks(this)
+ whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
+ whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+ whenever(userContextProvider.userContext).thenReturn(mContext)
+ whenever(
+ userFileManager.getSharedPreferences(
+ eq(RecordIssueTile.TILE_SPEC),
+ eq(Context.MODE_PRIVATE),
+ anyInt()
+ )
+ )
+ .thenReturn(sharedPreferences)
+
+ factory =
+ spy(
+ SystemUIDialog.Factory(
+ context,
+ flags,
+ systemUIDialogManager,
+ sysuiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator
+ )
)
latch = CountDownLatch(1)
dialog =
- RecordIssueDialogDelegate(dialogFactory, mock()) { latch.countDown() }.createDialog()
+ RecordIssueDialogDelegate(
+ factory,
+ userContextProvider,
+ userTracker,
+ flags,
+ bgExecutor,
+ mainExecutor,
+ dprLazy,
+ mediaProjectionMetricsLogger,
+ userFileManager,
+ ) {
+ latch.countDown()
+ }
+ .createDialog()
dialog.show()
}
@@ -91,4 +153,82 @@
dialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick()
latch.await(1L, TimeUnit.MILLISECONDS)
}
+
+ @Test
+ fun screenCaptureDisabledDialog_isShown_whenFunctionalityIsDisabled() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(true)
+ whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+ .thenReturn(true)
+
+ val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+ screenRecordSwitch.isChecked = true
+
+ val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(bgExecutor).execute(bgCaptor.capture())
+ bgCaptor.value.run()
+
+ val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(mainExecutor).execute(mainCaptor.capture())
+ mainCaptor.value.run()
+
+ verify(mediaProjectionMetricsLogger, never())
+ .notifyProjectionInitiated(
+ anyInt(),
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ )
+ assertThat(screenRecordSwitch.isChecked).isFalse()
+ }
+
+ @Test
+ fun screenCapturePermissionDialog_isShown_correctly() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(false)
+ whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+ .thenReturn(false)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+ whenever(sharedPreferences.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false))
+ .thenReturn(false)
+
+ val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+ screenRecordSwitch.isChecked = true
+
+ val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(bgExecutor).execute(bgCaptor.capture())
+ bgCaptor.value.run()
+
+ val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(mainExecutor).execute(mainCaptor.capture())
+ mainCaptor.value.run()
+
+ verify(mediaProjectionMetricsLogger)
+ .notifyProjectionInitiated(
+ anyInt(),
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ )
+ verify(factory).create(any<ScreenCapturePermissionDialogDelegate>())
+ }
+
+ @Test
+ fun noDialogsAreShown_forScreenRecord_whenApprovalIsAlreadyGiven() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(false)
+ whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+ .thenReturn(false)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
+
+ val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+ screenRecordSwitch.isChecked = true
+
+ val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(bgExecutor).execute(bgCaptor.capture())
+ bgCaptor.value.run()
+
+ verify(mediaProjectionMetricsLogger)
+ .notifyProjectionInitiated(
+ anyInt(),
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ )
+ verify(factory, never()).create(any<ScreenCapturePermissionDialogDelegate>())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ee94cbb..ee27c5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.flags.Flags.TRANSIT_CLOCK
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProviderPlugin
import com.android.systemui.plugins.clocks.ClockSettings
@@ -128,6 +129,7 @@
override fun createClock(settings: ClockSettings): ClockController =
createCallbacks[settings.clockId!!]!!(settings.clockId!!)
override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id)
+ override fun initialize(buffers: ClockMessageBuffers?) { }
fun addClock(
id: ClockId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index fef262f..e0e8d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -26,6 +26,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.customization.R
+import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
import com.android.systemui.util.mockito.any
@@ -49,6 +50,9 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController =
+ createClock(ClockSettings(id, null)) as DefaultClockController
+
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DefaultClockProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 14751c2..54d3607 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -36,6 +36,7 @@
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -91,7 +92,6 @@
private NotificationIconAreaController mMockNotificationAreaController;
private ShadeExpansionStateManager mShadeExpansionStateManager;
- private View mNotificationAreaInner;
private OngoingCallController mOngoingCallController;
private SystemStatusAnimationScheduler mAnimationScheduler;
private StatusBarLocationPublisher mLocationPublisher;
@@ -270,15 +270,15 @@
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@@ -310,7 +310,7 @@
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -326,7 +326,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -343,7 +343,7 @@
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the shade is updated to no longer be open
@@ -354,7 +354,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -368,7 +368,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -382,7 +382,7 @@
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -396,7 +396,7 @@
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the transition has finished
@@ -405,7 +405,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -438,7 +438,7 @@
assertEquals(View.VISIBLE,
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@@ -503,8 +503,8 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
// Notification area is hidden without delay
- assertEquals(0f, mNotificationAreaInner.getAlpha(), 0.01);
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@@ -723,11 +723,10 @@
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
-
- mNotificationAreaInner = new View(mContext);
-
- when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
- mNotificationAreaInner);
+ View notificationAreaInner =
+ LayoutInflater.from(mContext).inflate(R.layout.notification_icon_area, null);
+ when(mMockNotificationAreaController.getNotificationInnerAreaView())
+ .thenReturn(notificationAreaInner);
}
/**
@@ -782,4 +781,8 @@
private View getEndSideContentView() {
return mFragment.getView().findViewById(R.id.status_bar_end_side_content);
}
+
+ private View getNotificationAreaView() {
+ return mFragment.getView().findViewById(R.id.notificationIcons);
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index d9741c8..4a6d5c9b 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -25,9 +25,6 @@
import android.service.autofill.FillResponse;
import android.util.Slog;
-import java.util.Objects;
-
-
/**
* Requests autofill response from a Remote Autofill Service. This autofill service can be
* either a Credential Autofill Service or the user-opted autofill service.
@@ -51,7 +48,6 @@
private final RemoteFillService mRemoteFillService;
private final SecondaryProviderCallback mCallback;
- private FillRequest mLastFillRequest;
private int mLastFlag;
SecondaryProviderHandler(
@@ -97,17 +93,11 @@
}
/**
- * Requests a new fill response. If the fill request is same as the last requested fill request,
- * then the request is duped.
+ * Requests a new fill response.
*/
public void onFillRequest(FillRequest pendingFillRequest, int flag) {
- if (Objects.equals(pendingFillRequest, mLastFillRequest)) {
- Slog.v(TAG, "Deduping fill request to secondary provider.");
- return;
- }
Slog.v(TAG, "Requesting fill response to secondary provider.");
mLastFlag = flag;
- mLastFillRequest = pendingFillRequest;
mRemoteFillService.onFillRequest(pendingFillRequest);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d527ce0..c4e8f12 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -367,6 +367,9 @@
@GuardedBy("mLock")
private SparseArray<FillResponse> mResponses;
+ @GuardedBy("mLock")
+ private SparseArray<FillResponse> mSecondaryResponses;
+
/**
* Contexts read from the app; they will be updated (sanitized, change values for save) before
* sent to {@link AutofillService}. Ordered by the time they were read.
@@ -713,7 +716,14 @@
mPendingFillRequest.getDelayedFillIntentSender());
}
mLastFillRequest = mPendingFillRequest;
- mRemoteFillService.onFillRequest(mPendingFillRequest);
+ if (shouldRequestSecondaryProvider(mPendingFillRequest.getFlags())
+ && mSecondaryProviderHandler != null) {
+ Slog.v(TAG, "Requesting fill response to secondary provider.");
+ mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
+ mPendingFillRequest.getFlags());
+ } else if (mRemoteFillService != null) {
+ mRemoteFillService.onFillRequest(mPendingFillRequest);
+ }
mPendingInlineSuggestionsRequest = null;
mWaitForInlineRequest = false;
mPendingFillRequest = null;
@@ -1196,7 +1206,8 @@
@GuardedBy("mLock")
private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
int flags) {
- final FillResponse existingResponse = viewState.getResponse();
+ final FillResponse existingResponse = shouldRequestSecondaryProvider(flags)
+ ? viewState.getSecondaryResponse() : viewState.getResponse();
mFillRequestEventLogger.startLogForNewRequest();
mRequestCount++;
mFillRequestEventLogger.maybeSetAppPackageUid(uid);
@@ -1804,6 +1815,10 @@
return;
}
synchronized (mLock) {
+ if (mSecondaryResponses == null) {
+ mSecondaryResponses = new SparseArray<>(2);
+ }
+ mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse);
setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
/* isPrimary= */ false);
@@ -3980,7 +3995,7 @@
}
// If it's not, then check if it should start a partition.
- if (shouldStartNewPartitionLocked(id)) {
+ if (shouldStartNewPartitionLocked(id, flags)) {
if (sDebug) {
Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
+ viewState.getStateAsString());
@@ -4008,9 +4023,11 @@
* @return {@code true} if a new partition should be started
*/
@GuardedBy("mLock")
- private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
+ private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) {
final ViewState currentView = mViewStates.get(id);
- if (mResponses == null) {
+ SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags)
+ ? mSecondaryResponses : mResponses;
+ if (responses == null) {
return currentView != null && (currentView.getState()
& ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0;
}
@@ -4022,7 +4039,7 @@
return true;
}
- final int numResponses = mResponses.size();
+ final int numResponses = responses.size();
if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
+ " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
@@ -4030,7 +4047,7 @@
}
for (int responseNum = 0; responseNum < numResponses; responseNum++) {
- final FillResponse response = mResponses.valueAt(responseNum);
+ final FillResponse response = responses.valueAt(responseNum);
if (ArrayUtils.contains(response.getIgnoredIds(), id)) {
return false;
@@ -4066,6 +4083,10 @@
}
boolean shouldRequestSecondaryProvider(int flags) {
+ if (!mService.isAutofillCredmanIntegrationEnabled()
+ || mSecondaryProviderHandler == null) {
+ return false;
+ }
if (mIsPrimaryCredential) {
return (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) == 0;
} else {
@@ -4205,12 +4226,6 @@
}
break;
case ACTION_VIEW_ENTERED:
- if (shouldRequestSecondaryProvider(flags)
- && mSecondaryProviderHandler != null
- && mAssistReceiver.mLastFillRequest != null) {
- mSecondaryProviderHandler.onFillRequest(mAssistReceiver.mLastFillRequest,
- flags);
- }
mLatencyBaseTime = SystemClock.elapsedRealtime();
boolean wasPreviouslyFillDialog = mPreviouslyFillDialogPotentiallyStarted;
mPreviouslyFillDialogPotentiallyStarted = false;
@@ -4225,6 +4240,19 @@
viewState.setCurrentValue(value);
}
+ if (shouldRequestSecondaryProvider(flags)) {
+ if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+ id, viewState, flags)) {
+ Slog.v(TAG, "Started a new fill request for secondary provider.");
+ return;
+ }
+ // If the ViewState is ready to be displayed, onReady() will be called.
+ viewState.update(value, virtualBounds, flags);
+
+ // return here because primary provider logic is not applicable.
+ return;
+ }
+
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
return;
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index b0bb9ec..fec5aa5 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -162,6 +162,11 @@
return mPrimaryFillResponse;
}
+ @Nullable
+ FillResponse getSecondaryResponse() {
+ return mSecondaryFillResponse;
+ }
+
void setResponse(FillResponse response) {
setResponse(response, /* isPrimary= */ true);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3ce91c8..1d69905 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -225,7 +225,7 @@
/**
* The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
*/
- private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index badd7f0..2b81dbc 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2059,9 +2059,6 @@
mTargetUserId = targetUserId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
- if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
- mInjector.setHasTopUi(true);
- }
if (userSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
@@ -2130,9 +2127,6 @@
}
private void endUserSwitch() {
- if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
- mInjector.setHasTopUi(false);
- }
final int nextUserId;
synchronized (mLock) {
nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
@@ -3816,15 +3810,6 @@
getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
}
- void setHasTopUi(boolean hasTopUi) {
- try {
- Slogf.i(TAG, "Setting hasTopUi to " + hasTopUi);
- mService.setHasTopUi(hasTopUi);
- } catch (RemoteException e) {
- Slogf.e(TAG, "Failed to allow using all CPU cores", e);
- }
- }
-
void onSystemUserVisibilityChanged(boolean visible) {
getUserManagerInternal().onSystemUserVisibilityChanged(visible);
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 6a4b00f..8405e0a 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -23,12 +23,14 @@
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessCorrection;
import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.LongArray;
import android.util.MathUtils;
import android.util.Pair;
@@ -80,45 +82,50 @@
* Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle
* mode.
*
- * @param resources
+ * @param context
* @param displayDeviceConfig
* @param mode The auto-brightness mode. Different modes use different brightness curves
* @param displayWhiteBalanceController
* @return the BrightnessMappingStrategy
*/
@Nullable
- static BrightnessMappingStrategy create(Resources resources,
+ static BrightnessMappingStrategy create(Context context,
DisplayDeviceConfig displayDeviceConfig,
@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
- DisplayWhiteBalanceController displayWhiteBalanceController) {
+ @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
// Display independent, mode dependent values
float[] brightnessLevelsNits = null;
float[] brightnessLevels = null;
float[] luxLevels = null;
+ int preset = Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
switch (mode) {
case AUTO_BRIGHTNESS_MODE_DEFAULT -> {
brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
- luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode);
- brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode);
+ luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
+ brightnessLevels =
+ displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
}
case AUTO_BRIGHTNESS_MODE_IDLE -> {
- brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
+ brightnessLevelsNits = getFloatArray(context.getResources().obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle));
- luxLevels = getLuxLevels(resources.getIntArray(
+ luxLevels = getLuxLevels(context.getResources().getIntArray(
com.android.internal.R.array.config_autoBrightnessLevelsIdle));
}
case AUTO_BRIGHTNESS_MODE_DOZE -> {
- luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode);
- brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode);
+ luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
+ brightnessLevels =
+ displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
}
}
// Display independent, mode independent values
- float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
+ float autoBrightnessAdjustmentMaxGamma = context.getResources().getFraction(
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
1, 1);
- long shortTermModelTimeout = resources.getInteger(
+ long shortTermModelTimeout = context.getResources().getInteger(
com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
// Display dependent values - used for physical mapping strategy nits -> brightness
@@ -823,6 +830,8 @@
private float mAutoBrightnessAdjustment;
private float mUserLux;
private float mUserBrightness;
+
+ @Nullable
private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
@AutomaticBrightnessController.AutomaticBrightnessMode
@@ -838,7 +847,7 @@
public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
float[] brightness, float maxGamma,
@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
- DisplayWhiteBalanceController displayWhiteBalanceController) {
+ @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
"Nits and brightness arrays must not be empty!");
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a6f42d7..bd22e1d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1592,24 +1592,13 @@
/**
* @param mode The auto-brightness mode
- * @return The default auto-brightness brightening ambient lux levels for the specified mode
- * and the normal brightness preset
- */
- public float[] getAutoBrightnessBrighteningLevelsLux(
- @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
- if (mDisplayBrightnessMapping == null) {
- return null;
- }
- return mDisplayBrightnessMapping.getLuxArray(mode);
- }
-
- /**
- * @param mode The auto-brightness mode
* @param preset The brightness preset. Presets are used on devices that allow users to choose
* from a set of predefined options in display auto-brightness settings.
- * @return Auto brightness brightening ambient lux levels for the specified mode and preset
+ * @return The default auto-brightness brightening ambient lux levels for the specified mode
+ * and preset
*/
- public float[] getAutoBrightnessBrighteningLevelsLux(String mode, String preset) {
+ public float[] getAutoBrightnessBrighteningLevelsLux(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
if (mDisplayBrightnessMapping == null) {
return null;
}
@@ -1628,24 +1617,12 @@
/**
* @param mode The auto-brightness mode
- * @return The default auto-brightness brightening levels for the specified mode and the normal
- * brightness preset
- */
- public float[] getAutoBrightnessBrighteningLevels(
- @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
- if (mDisplayBrightnessMapping == null) {
- return null;
- }
- return mDisplayBrightnessMapping.getBrightnessArray(mode);
- }
-
- /**
- * @param mode The auto-brightness mode
* @param preset The brightness preset. Presets are used on devices that allow users to choose
* from a set of predefined options in display auto-brightness settings.
- * @return Auto brightness brightening backlight levels for the specified mode and preset
+ * @return The default auto-brightness brightening levels for the specified mode and preset
*/
- public float[] getAutoBrightnessBrighteningLevels(String mode, String preset) {
+ public float[] getAutoBrightnessBrighteningLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
if (mDisplayBrightnessMapping == null) {
return null;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2685efe..06e5f99 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -735,7 +735,7 @@
mCdsi = null;
}
- setUpAutoBrightness(resources, handler);
+ setUpAutoBrightness(context, handler);
mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic()
&& !resources.getBoolean(
@@ -1075,7 +1075,7 @@
loadBrightnessRampRates();
loadProximitySensor();
loadNitsRange(mContext.getResources());
- setUpAutoBrightness(mContext.getResources(), mHandler);
+ setUpAutoBrightness(mContext, mHandler);
reloadReduceBrightColours();
setAnimatorRampSpeeds(/* isIdleMode= */ false);
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
@@ -1145,7 +1145,7 @@
handleBrightnessModeChange();
}
- private void setUpAutoBrightness(Resources resources, Handler handler) {
+ private void setUpAutoBrightness(Context context, Handler handler) {
mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
if (!mUseSoftwareAutoBrightnessConfig) {
@@ -1155,21 +1155,19 @@
SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>();
BrightnessMappingStrategy defaultModeBrightnessMapper =
- mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig,
+ mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig,
mDisplayWhiteBalanceController);
brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
defaultModeBrightnessMapper);
- final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean(
R.bool.config_enableIdleScreenBrightnessMode);
if (isIdleScreenBrightnessEnabled) {
BrightnessMappingStrategy idleModeBrightnessMapper =
- BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig,
- AUTO_BRIGHTNESS_MODE_IDLE,
- mDisplayWhiteBalanceController);
+ BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController);
if (idleModeBrightnessMapper != null) {
- brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE,
- idleModeBrightnessMapper);
+ brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, idleModeBrightnessMapper);
}
}
@@ -1181,7 +1179,7 @@
}
if (defaultModeBrightnessMapper != null) {
- final float dozeScaleFactor = resources.getFraction(
+ final float dozeScaleFactor = context.getResources().getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -1265,14 +1263,14 @@
.getAutoBrightnessBrighteningLightDebounceIdle();
long darkeningLightDebounceIdle = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounceIdle();
- boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
- int lightSensorWarmUpTimeConfig = resources.getInteger(
+ int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
com.android.internal.R.integer.config_lightSensorWarmupTime);
- int lightSensorRate = resources.getInteger(
+ int lightSensorRate = context.getResources().getInteger(
com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
- int initialLightSensorRate = resources.getInteger(
+ int initialLightSensorRate = context.getResources().getInteger(
com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
if (initialLightSensorRate == -1) {
initialLightSensorRate = lightSensorRate;
@@ -3645,12 +3643,11 @@
userNits);
}
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
- return BrightnessMappingStrategy.create(resources,
- displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT,
- displayWhiteBalanceController);
+ return BrightnessMappingStrategy.create(context, displayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController);
}
HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index c088a6d..519224a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -19,6 +19,7 @@
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -624,7 +625,7 @@
mCdsi = null;
}
- setUpAutoBrightness(resources, handler);
+ setUpAutoBrightness(context, handler);
mColorFadeEnabled = mInjector.isColorFadeEnabled()
&& !resources.getBoolean(
@@ -905,7 +906,7 @@
// updated here.
loadBrightnessRampRates();
loadNitsRange(mContext.getResources());
- setUpAutoBrightness(mContext.getResources(), mHandler);
+ setUpAutoBrightness(mContext, mHandler);
reloadReduceBrightColours();
setAnimatorRampSpeeds(/* isIdleMode= */ false);
@@ -976,10 +977,15 @@
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE),
false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ if (mFlags.areAutoBrightnessModesEnabled()) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS),
+ /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT);
+ }
handleBrightnessModeChange();
}
- private void setUpAutoBrightness(Resources resources, Handler handler) {
+ private void setUpAutoBrightness(Context context, Handler handler) {
mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
if (!mUseSoftwareAutoBrightnessConfig) {
@@ -989,16 +995,16 @@
SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>();
BrightnessMappingStrategy defaultModeBrightnessMapper =
- mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig,
+ mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig,
mDisplayWhiteBalanceController);
brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
defaultModeBrightnessMapper);
- final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean(
R.bool.config_enableIdleScreenBrightnessMode);
if (isIdleScreenBrightnessEnabled) {
BrightnessMappingStrategy idleModeBrightnessMapper =
- BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig,
+ BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
AUTO_BRIGHTNESS_MODE_IDLE,
mDisplayWhiteBalanceController);
if (idleModeBrightnessMapper != null) {
@@ -1008,7 +1014,7 @@
}
BrightnessMappingStrategy dozeModeBrightnessMapper =
- BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig,
+ BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController);
if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) {
brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper);
@@ -1022,7 +1028,7 @@
}
if (defaultModeBrightnessMapper != null) {
- final float dozeScaleFactor = resources.getFraction(
+ final float dozeScaleFactor = context.getResources().getFraction(
R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -1106,14 +1112,14 @@
.getAutoBrightnessBrighteningLightDebounceIdle();
long darkeningLightDebounceIdle = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounceIdle();
- boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
- int lightSensorWarmUpTimeConfig = resources.getInteger(
+ int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
R.integer.config_lightSensorWarmupTime);
- int lightSensorRate = resources.getInteger(
+ int lightSensorRate = context.getResources().getInteger(
R.integer.config_autoBrightnessLightSensorRate);
- int initialLightSensorRate = resources.getInteger(
+ int initialLightSensorRate = context.getResources().getInteger(
R.integer.config_autoBrightnessInitialLightSensorRate);
if (initialLightSensorRate == -1) {
initialLightSensorRate = lightSensorRate;
@@ -2999,6 +3005,16 @@
public void onChange(boolean selfChange, Uri uri) {
if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) {
handleBrightnessModeChange();
+ } else if (uri.equals(Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) {
+ int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL,
+ UserHandle.USER_CURRENT);
+ Slog.i(mTag, "Setting up auto-brightness for preset "
+ + autoBrightnessPresetToString(preset));
+ setUpAutoBrightness(mContext, mHandler);
+ sendUpdatePowerState();
} else {
handleSettingsChange(false /* userSwitch */);
}
@@ -3117,12 +3133,11 @@
userNits);
}
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
- return BrightnessMappingStrategy.create(resources,
- displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT,
- displayWhiteBalanceController);
+ return BrightnessMappingStrategy.create(context, displayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController);
}
HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
diff --git a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
index 8f12329..6978686 100644
--- a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
+++ b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.os.PowerManager;
+import android.provider.Settings;
import android.util.Spline;
import com.android.internal.display.BrightnessSynchronizer;
@@ -38,9 +39,9 @@
*/
public class DisplayBrightnessMappingConfig {
- private static final String DEFAULT_BRIGHTNESS_PRESET_NAME = "normal";
private static final String DEFAULT_BRIGHTNESS_MAPPING_KEY =
- AutoBrightnessModeName._default.getRawName() + "_" + DEFAULT_BRIGHTNESS_PRESET_NAME;
+ AutoBrightnessModeName._default.getRawName() + "_"
+ + AutoBrightnessSettingName.normal.getRawName();
/**
* Array of desired screen brightness in nits corresponding to the lux values
@@ -152,22 +153,15 @@
/**
* @param mode The auto-brightness mode
- * @return The default auto-brightness brightening ambient lux levels for the specified mode
- * and the normal brightness preset
- */
- public float[] getLuxArray(@AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
- return mBrightnessLevelsLuxMap.get(
- autoBrightnessModeToString(mode) + "_" + DEFAULT_BRIGHTNESS_PRESET_NAME);
- }
-
- /**
- * @param mode The auto-brightness mode
* @param preset The brightness preset. Presets are used on devices that allow users to choose
* from a set of predefined options in display auto-brightness settings.
- * @return Auto brightness brightening ambient lux levels for the specified mode and preset
+ * @return The default auto-brightness brightening ambient lux levels for the specified mode
+ * and preset
*/
- public float[] getLuxArray(String mode, String preset) {
- return mBrightnessLevelsLuxMap.get(mode + "_" + preset);
+ public float[] getLuxArray(@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
+ int preset) {
+ return mBrightnessLevelsLuxMap.get(
+ autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset));
}
/**
@@ -179,23 +173,14 @@
/**
* @param mode The auto-brightness mode
- * @return The default auto-brightness brightening levels for the specified mode and the normal
- * brightness preset
- */
- public float[] getBrightnessArray(
- @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
- return mBrightnessLevelsMap.get(
- autoBrightnessModeToString(mode) + "_" + DEFAULT_BRIGHTNESS_PRESET_NAME);
- }
-
- /**
- * @param mode The auto-brightness mode
* @param preset The brightness preset. Presets are used on devices that allow users to choose
* from a set of predefined options in display auto-brightness settings.
- * @return Auto brightness brightening ambient lux levels for the specified mode and preset
+ * @return The default auto-brightness brightening levels for the specified mode and preset
*/
- public float[] getBrightnessArray(String mode, String preset) {
- return mBrightnessLevelsMap.get(mode + "_" + preset);
+ public float[] getBrightnessArray(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
+ return mBrightnessLevelsMap.get(
+ autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset));
}
@Override
@@ -247,6 +232,24 @@
}
}
+ /**
+ * @param preset The brightness preset. Presets are used on devices that allow users to choose
+ * from a set of predefined options in display auto-brightness settings.
+ * @return The string representing the preset
+ */
+ public static String autoBrightnessPresetToString(int preset) {
+ return switch (preset) {
+ case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM ->
+ AutoBrightnessSettingName.dim.getRawName();
+ case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL ->
+ AutoBrightnessSettingName.normal.getRawName();
+ case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT ->
+ AutoBrightnessSettingName.bright.getRawName();
+ default -> throw new IllegalArgumentException(
+ "Unknown auto-brightness preset value: " + preset);
+ };
+ }
+
private float[] brightnessArrayIntToFloat(int[] brightnessInt,
Spline backlightToBrightnessSpline) {
float[] brightnessFloat = new float[brightnessInt.length];
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 64abb81..81204ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -1314,7 +1314,6 @@
*/
protected void disableDevice(
boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
- removeAction(AbsoluteVolumeAudioStatusAction.class);
removeAction(SetAudioVolumeLevelDiscoveryAction.class);
removeAction(ActiveSourceAction.class);
removeAction(ResendCecCommandAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 29303aa..6157402 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -308,7 +308,6 @@
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
removeAction(OneTouchPlayAction.class);
removeAction(DevicePowerStatusAction.class);
- removeAction(AbsoluteVolumeAudioStatusAction.class);
super.disableDevice(initiatedByCec, callback);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 5831b29..1cd267d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1336,7 +1336,6 @@
removeAction(OneTouchRecordAction.class);
removeAction(TimerRecordingAction.class);
removeAction(NewDeviceAction.class);
- removeAction(AbsoluteVolumeAudioStatusAction.class);
// Remove pending actions.
removeAction(RequestActiveSourceAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9253706..eaf754d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -3793,6 +3793,11 @@
}
}
});
+
+ // Make sure we switch away from absolute volume behavior (AVB) when entering standby.
+ // We do this because AVB should not be used unless AbsoluteVolumeAudioStatusAction exists,
+ // and the action cannot exist in standby because there are no local devices.
+ checkAndUpdateAbsoluteVolumeBehavior();
}
boolean canGoToStandby() {
@@ -4446,10 +4451,11 @@
* This allows the volume level of the System Audio device to be tracked and set by Android.
*
* Absolute volume behavior requires the following conditions:
- * 1. If the System Audio Device is an Audio System: System Audio Mode is active
- * 2. All AVB-capable audio output devices are already using full/absolute volume behavior
- * 3. CEC volume is enabled
- * 4. The System Audio device supports the <Set Audio Volume Level> message
+ * 1. The device is not in standby or transient to standby
+ * 2. If the System Audio Device is an Audio System: System Audio Mode is active
+ * 3. All AVB-capable audio output devices are already using full/absolute volume behavior
+ * 4. CEC volume is enabled
+ * 5. The System Audio device supports the <Set Audio Volume Level> message
*
* This method enables adjust-only absolute volume behavior on TV panels when conditions
* 1, 2, and 3 are met, but condition 4 is not. This allows TVs to track the volume level of
@@ -4465,10 +4471,16 @@
return;
}
+ // Condition 1: The device is not in standby or transient to standby
+ if (mPowerStatusController != null && isPowerStandbyOrTransient()) {
+ switchToFullVolumeBehavior();
+ return;
+ }
+
HdmiCecLocalDevice localCecDevice;
if (isTvDevice() && tv() != null) {
localCecDevice = tv();
- // Condition 1: TVs need System Audio Mode to be active
+ // Condition 2: TVs need System Audio Mode to be active
// (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the
// TV is the System Audio Device instead.)
if (!isSystemAudioActivated()) {
@@ -4485,7 +4497,7 @@
HdmiDeviceInfo systemAudioDeviceInfo = getDeviceInfo(
localCecDevice.findAudioReceiverAddress());
- // Condition 2: All AVB-capable audio outputs already use full/absolute volume behavior
+ // Condition 3: All AVB-capable audio outputs already use full/absolute volume behavior
// We only need to check the first AVB-capable audio output because only TV panels
// have more than one of them, and they always have the same volume behavior.
@AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
@@ -4493,7 +4505,7 @@
boolean alreadyUsingFullOrAbsoluteVolume =
FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS.contains(currentVolumeBehavior);
- // Condition 3: CEC volume is enabled
+ // Condition 4: CEC volume is enabled
boolean cecVolumeEnabled =
getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED;
@@ -4509,7 +4521,7 @@
return;
}
- // Condition 4: The System Audio device supports <Set Audio Volume Level>
+ // Condition 5: The System Audio device supports <Set Audio Volume Level>
switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
case DeviceFeatures.FEATURE_SUPPORTED:
if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
@@ -4556,6 +4568,8 @@
* are currently used. Removes the action for handling volume updates for these behaviors.
*/
private void switchToFullVolumeBehavior() {
+ Slog.d(TAG, "Switching to full volume behavior");
+
if (playback() != null) {
playback().removeAvbAudioStatusAction();
} else if (tv() != null) {
@@ -4597,12 +4611,14 @@
// Otherwise, enable adjust-only AVB on TVs only.
if (systemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
== DeviceFeatures.FEATURE_SUPPORTED) {
+ Slog.d(TAG, "Enabling absolute volume behavior");
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
device, volumeInfo, mServiceThreadExecutor,
mAbsoluteVolumeChangedListener, true);
}
} else if (tv() != null) {
+ Slog.d(TAG, "Enabling adjust-only absolute volume behavior");
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior(
device, volumeInfo, mServiceThreadExecutor,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 773293f..a6c5ad5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -356,15 +356,6 @@
new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR));
}
- List<String> getEnabledInputMethodNames() {
- List<String> result = new ArrayList<>();
- for (Pair<String, ArrayList<String>> pair :
- getEnabledInputMethodsAndSubtypeListLocked()) {
- result.add(pair.first);
- }
- return result;
- }
-
void appendAndPutEnabledInputMethodLocked(String id) {
if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
// Add in the newly enabled input method.
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index f2dcba4..5514ec7 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -59,6 +59,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -67,7 +68,7 @@
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.pm.PackageManagerServiceUtils;
-import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.PackageParserUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -141,7 +142,7 @@
return new AppIntegrityManagerServiceImpl(
context,
LocalServices.getService(PackageManagerInternal.class),
- PackageParser2::forParsingFileWithDefaults,
+ PackageParserUtils::forParsingFileWithDefaults,
RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(),
handlerThread.getThreadHandler());
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 8504495..0eb9166 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -87,6 +87,8 @@
@NonNull private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallbackImpl();
+ @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus;
+
@NonNull
private final AudioManager.OnDevicesForAttributesChangedListener
mOnDevicesForAttributesChangedListener = this::onDevicesForAttributesChangedListener;
@@ -113,6 +115,10 @@
mHandler = new Handler(Objects.requireNonNull(looper));
mStrategyForMedia = Objects.requireNonNull(strategyForMedia);
mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener);
+
+ mBuiltInSpeakerSuitabilityStatus =
+ DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
+
mBluetoothRouteController =
new AudioPoliciesBluetoothRouteController(
mContext, btAdapter, this::rebuildAvailableRoutesAndNotify);
@@ -373,14 +379,19 @@
// from getting an id using BluetoothRouteController#getRouteIdForBluetoothAddress.
routeId = systemRouteInfo.mDefaultRouteId;
}
- return new MediaRoute2Info.Builder(routeId, humanReadableName)
+ MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(routeId, humanReadableName)
.setType(systemRouteInfo.mMediaRoute2InfoType)
.setAddress(address)
.setSystemRoute(true)
.addFeature(FEATURE_LIVE_AUDIO)
.addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+
+ if (systemRouteInfo.mMediaRoute2InfoType == MediaRoute2Info.TYPE_BUILTIN_SPEAKER) {
+ builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus);
+ }
+
+ return builder.build();
}
/**
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 9f175a9..8b62cc9 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -81,6 +81,30 @@
}
}
+ /** Returns device route availability status. */
+ @MediaRoute2Info.SuitabilityStatus
+ static int getBuiltInSpeakerSuitabilityStatus(@NonNull Context context) {
+ if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ // Route is always suitable if the flag is disabled.
+ return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+ }
+
+ int availabilityStatus =
+ context.getResources()
+ .getInteger(
+ com.android.internal.R.integer
+ .config_mediaRouter_builtInSpeakerSuitability);
+
+ switch (availabilityStatus) {
+ case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER:
+ case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER:
+ case MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER:
+ return availabilityStatus;
+ default:
+ return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+ }
+ }
+
/** Returns the currently selected device (built-in or wired) route. */
@NonNull
MediaRoute2Info getSelectedRoute();
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index c0f2834..65b0ad0 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -72,6 +72,8 @@
@NonNull
private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+ @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus;
+
private int mDeviceVolume;
private MediaRoute2Info mDeviceRoute;
@@ -90,6 +92,9 @@
mAudioManager = audioManager;
mAudioService = audioService;
+ mBuiltInSpeakerSuitabilityStatus =
+ DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
+
AudioRoutesInfo newAudioRoutes = null;
try {
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
@@ -165,19 +170,28 @@
}
synchronized (this) {
- return new MediaRoute2Info.Builder(
- DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
- .setVolumeHandling(mAudioManager.isVolumeFixed()
- ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
- : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolume(mDeviceVolume)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setType(type)
- .addFeature(FEATURE_LIVE_AUDIO)
- .addFeature(FEATURE_LIVE_VIDEO)
- .addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
+ MediaRoute2Info.Builder builder =
+ new MediaRoute2Info.Builder(
+ DEVICE_ROUTE_ID,
+ mContext.getResources().getText(name).toString())
+ .setVolumeHandling(
+ mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mDeviceVolume)
+ .setVolumeMax(
+ mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setType(type)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LIVE_VIDEO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+
+ if (type == TYPE_BUILTIN_SPEAKER) {
+ builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus);
+ }
+
+ return builder.build();
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 8149847..1bc2a5e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -24,6 +24,7 @@
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import com.android.internal.annotations.GuardedBy;
@@ -54,8 +55,15 @@
mCallback = callback;
}
- public abstract void requestCreateSession(long requestId, String packageName, String routeId,
- @Nullable Bundle sessionHints);
+ public abstract void requestCreateSession(
+ long requestId,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName);
+
public abstract void releaseSession(long requestId, String sessionId);
public abstract void updateDiscoveryPreference(
@@ -63,7 +71,14 @@
public abstract void selectRoute(long requestId, String sessionId, String routeId);
public abstract void deselectRoute(long requestId, String sessionId, String routeId);
- public abstract void transferToRoute(long requestId, String sessionId, String routeId);
+
+ public abstract void transferToRoute(
+ long requestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ String sessionId,
+ String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason);
public abstract void setRouteVolume(long requestId, String routeId, int volume);
public abstract void setSessionVolume(long requestId, String sessionId, int volume);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 330818e..ae889d8 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -98,8 +98,14 @@
}
@Override
- public void requestCreateSession(long requestId, String packageName, String routeId,
- Bundle sessionHints) {
+ public void requestCreateSession(
+ long requestId,
+ String packageName,
+ String routeId,
+ Bundle sessionHints,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
if (mConnectionReady) {
mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints);
updateBinding();
@@ -141,7 +147,13 @@
}
@Override
- public void transferToRoute(long requestId, String sessionId, String routeId) {
+ public void transferToRoute(
+ long requestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ String sessionId,
+ String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason) {
if (mConnectionReady) {
mActiveConnection.transferToRoute(requestId, sessionId, routeId);
}
@@ -649,6 +661,14 @@
+ "Disallowed route: "
+ route);
}
+
+ if (route.getSuitabilityStatus()
+ == MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER) {
+ throw new SecurityException(
+ "Only the system is allowed to set not suitable for transfer status. "
+ + "Disallowed route: "
+ + route);
+ }
}
Connection connection = mConnectionRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5e18727..38f0df4 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -337,18 +337,47 @@
}
}
- public void requestCreateSessionWithRouter2(@NonNull IMediaRouter2 router, int requestId,
- long managerRequestId, @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route, Bundle sessionHints) {
+ public void requestCreateSessionWithRouter2(
+ @NonNull IMediaRouter2 router,
+ int requestId,
+ long managerRequestId,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ Bundle sessionHints,
+ @Nullable UserHandle transferInitiatorUserHandle,
+ @Nullable String transferInitiatorPackageName) {
Objects.requireNonNull(router, "router must not be null");
Objects.requireNonNull(oldSession, "oldSession must not be null");
Objects.requireNonNull(route, "route must not be null");
+ synchronized (mLock) {
+ if (managerRequestId == MediaRoute2ProviderService.REQUEST_ID_NONE
+ || transferInitiatorUserHandle == null
+ || transferInitiatorPackageName == null) {
+ final IBinder binder = router.asBinder();
+ final RouterRecord routerRecord = mAllRouterRecords.get(binder);
+
+ transferInitiatorUserHandle = Binder.getCallingUserHandle();
+ if (routerRecord != null) {
+ transferInitiatorPackageName = routerRecord.mPackageName;
+ } else {
+ transferInitiatorPackageName = mContext.getPackageName();
+ }
+ }
+ }
+
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionWithRouter2Locked(requestId, managerRequestId,
- router, oldSession, route, sessionHints);
+ requestCreateSessionWithRouter2Locked(
+ requestId,
+ managerRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ router,
+ oldSession,
+ route,
+ sessionHints);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -399,10 +428,11 @@
throw new IllegalArgumentException("uniqueSessionId must not be empty");
}
+ UserHandle userHandle = Binder.getCallingUserHandle();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- transferToRouteWithRouter2Locked(router, uniqueSessionId, route);
+ transferToRouteWithRouter2Locked(router, userHandle, uniqueSessionId, route);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -588,16 +618,28 @@
}
}
- public void requestCreateSessionWithManager(@NonNull IMediaRouter2Manager manager,
- int requestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+ public void requestCreateSessionWithManager(
+ @NonNull IMediaRouter2Manager manager,
+ int requestId,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(manager, "manager must not be null");
Objects.requireNonNull(oldSession, "oldSession must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
+ requestCreateSessionWithManagerLocked(
+ requestId,
+ manager,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -640,18 +682,32 @@
}
}
- public void transferToRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ public void transferToRouteWithManager(
+ @NonNull IMediaRouter2Manager manager,
+ int requestId,
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
}
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
+ Objects.requireNonNull(transferInitiatorPackageName);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- transferToRouteWithManagerLocked(requestId, manager, uniqueSessionId, route);
+ transferToRouteWithManagerLocked(
+ requestId,
+ manager,
+ uniqueSessionId,
+ route,
+ RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1038,9 +1094,15 @@
}
@GuardedBy("mLock")
- private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId,
- @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+ private void requestCreateSessionWithRouter2Locked(
+ int requestId,
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ @NonNull IMediaRouter2 router,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @Nullable Bundle sessionHints) {
final IBinder binder = router.asBinder();
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
@@ -1114,9 +1176,16 @@
long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId);
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler,
+ obtainMessage(
+ UserHandler::requestCreateSessionWithRouter2OnHandler,
routerRecord.mUserRecord.mHandler,
- uniqueRequestId, managerRequestId, routerRecord, oldSession, route,
+ uniqueRequestId,
+ managerRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ routerRecord,
+ oldSession,
+ route,
sessionHints));
}
@@ -1165,8 +1234,11 @@
}
@GuardedBy("mLock")
- private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ private void transferToRouteWithRouter2Locked(
+ @NonNull IMediaRouter2 router,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route) {
final IBinder binder = router.asBinder();
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
@@ -1191,9 +1263,16 @@
routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID)));
} else {
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::transferToRouteOnHandler,
+ obtainMessage(
+ UserHandler::transferToRouteOnHandler,
routerRecord.mUserRecord.mHandler,
- DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+ DUMMY_REQUEST_ID,
+ transferInitiatorUserHandle,
+ routerRecord.mPackageName,
+ routerRecord,
+ uniqueSessionId,
+ route,
+ RoutingSessionInfo.TRANSFER_REASON_APP));
}
}
@@ -1416,9 +1495,13 @@
}
@GuardedBy("mLock")
- private void requestCreateSessionWithManagerLocked(int requestId,
- @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route) {
+ private void requestCreateSessionWithManagerLocked(
+ int requestId,
+ @NonNull IMediaRouter2Manager manager,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
if (managerRecord == null) {
return;
@@ -1464,9 +1547,16 @@
// Before requesting to the provider, get session hints from the media router.
// As a return, media router will request to create a session.
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestRouterCreateSessionOnHandler,
+ obtainMessage(
+ UserHandler::requestRouterCreateSessionOnHandler,
routerRecord.mUserRecord.mHandler,
- uniqueRequestId, routerRecord, managerRecord, oldSession, route));
+ uniqueRequestId,
+ routerRecord,
+ managerRecord,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName));
}
@GuardedBy("mLock")
@@ -1521,9 +1611,14 @@
}
@GuardedBy("mLock")
- private void transferToRouteWithManagerLocked(int requestId,
+ private void transferToRouteWithManagerLocked(
+ int requestId,
@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1541,9 +1636,16 @@
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::transferToRouteOnHandler,
+ obtainMessage(
+ UserHandler::transferToRouteOnHandler,
managerRecord.mUserRecord.mHandler,
- uniqueRequestId, routerRecord, uniqueSessionId, route));
+ uniqueRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ routerRecord,
+ uniqueSessionId,
+ route,
+ transferReason));
}
@GuardedBy("mLock")
@@ -1850,6 +1952,19 @@
}
}
+ public void notifySessionCreated(int requestId, @NonNull RoutingSessionInfo sessionInfo) {
+ try {
+ mRouter.notifySessionCreated(
+ requestId, maybeClearTransferInitiatorIdentity(sessionInfo));
+ } catch (RemoteException ex) {
+ Slog.w(
+ TAG,
+ "Failed to notify router of the session creation."
+ + " Router probably died.",
+ ex);
+ }
+ }
+
/**
* Sends the corresponding router an update for the given session.
*
@@ -1857,12 +1972,27 @@
*/
public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) {
try {
- mRouter.notifySessionInfoChanged(sessionInfo);
+ mRouter.notifySessionInfoChanged(maybeClearTransferInitiatorIdentity(sessionInfo));
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
}
}
+ private RoutingSessionInfo maybeClearTransferInitiatorIdentity(
+ @NonNull RoutingSessionInfo sessionInfo) {
+ UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
+ String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
+
+ if (!Objects.equals(UserHandle.of(mUserRecord.mUserId), transferInitiatorUserHandle)
+ || !Objects.equals(mPackageName, transferInitiatorPackageName)) {
+ return new RoutingSessionInfo.Builder(sessionInfo)
+ .setTransferInitiator(null, null)
+ .build();
+ }
+
+ return sessionInfo;
+ }
+
/**
* Returns a filtered copy of {@code routes} that contains only the routes that are {@link
* MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
@@ -2307,9 +2437,14 @@
return -1;
}
- private void requestRouterCreateSessionOnHandler(long uniqueRequestId,
- @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord,
- @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+ private void requestRouterCreateSessionOnHandler(
+ long uniqueRequestId,
+ @NonNull RouterRecord routerRecord,
+ @NonNull ManagerRecord managerRecord,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
try {
if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission()) {
// The router lacks permission to modify system routing, so we hide system
@@ -2317,7 +2452,11 @@
route = mSystemProvider.getDefaultRoute();
}
routerRecord.mRouter.requestCreateSessionByManager(
- uniqueRequestId, oldSession, route);
+ uniqueRequestId,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
+ "Failed to request. Router probably died.", ex);
@@ -2326,10 +2465,15 @@
}
}
- private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId,
- long managerRequestId, @NonNull RouterRecord routerRecord,
+ private void requestCreateSessionWithRouter2OnHandler(
+ long uniqueRequestId,
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ @NonNull RouterRecord routerRecord,
@NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+ @NonNull MediaRoute2Info route,
+ @Nullable Bundle sessionHints) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
@@ -2345,8 +2489,19 @@
managerRequestId, oldSession, route);
mSessionCreationRequests.add(request);
- provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
- route.getOriginalId(), sessionHints);
+ int transferReason = RoutingSessionInfo.TRANSFER_REASON_APP;
+ if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
+ transferReason = RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST;
+ }
+
+ provider.requestCreateSession(
+ uniqueRequestId,
+ routerRecord.mPackageName,
+ route.getOriginalId(),
+ sessionHints,
+ transferReason,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// routerRecord can be null if the session is system's or RCN.
@@ -2386,9 +2541,14 @@
}
// routerRecord can be null if the session is system's or RCN.
- private void transferToRouteOnHandler(long uniqueRequestId,
+ private void transferToRouteOnHandler(
+ long uniqueRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
@Nullable RouterRecord routerRecord,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route,
+ @RoutingSessionInfo.TransferReason int transferReason) {
if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
"transferring to")) {
return;
@@ -2399,8 +2559,13 @@
if (provider == null) {
return;
}
- provider.transferToRoute(uniqueRequestId, getOriginalId(uniqueSessionId),
- route.getOriginalId());
+ provider.transferToRoute(
+ uniqueRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ getOriginalId(uniqueSessionId),
+ route.getOriginalId(),
+ transferReason);
}
// routerRecord is null if and only if the session is created without the request, which
@@ -2535,6 +2700,7 @@
// session info from them.
sessionInfo = mSystemProvider.getDefaultSessionInfo();
}
+ // TODO: b/279555229 - replace with matchingRequest.mRouterRecord.notifySessionCreated.
notifySessionCreatedToRouter(
matchingRequest.mRouterRecord,
toOriginalRequestId(uniqueRequestId),
@@ -2648,12 +2814,7 @@
private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord,
int requestId, @NonNull RoutingSessionInfo sessionInfo) {
- try {
- routerRecord.mRouter.notifySessionCreated(requestId, sessionInfo);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify router of the session creation."
- + " Router probably died.", ex);
- }
+ routerRecord.notifySessionCreated(requestId, sessionInfo);
}
private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index e562b3f..7dd1314 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -461,11 +461,24 @@
// Binder call
@Override
- public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
- long managerRequestId, RoutingSessionInfo oldSession,
- MediaRoute2Info route, Bundle sessionHints) {
- mService2.requestCreateSessionWithRouter2(router, requestId, managerRequestId,
- oldSession, route, sessionHints);
+ public void requestCreateSessionWithRouter2(
+ IMediaRouter2 router,
+ int requestId,
+ long managerRequestId,
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ Bundle sessionHints,
+ @Nullable UserHandle transferInitiatorUserHandle,
+ @Nullable String transferInitiatorPackageName) {
+ mService2.requestCreateSessionWithRouter2(
+ router,
+ requestId,
+ managerRequestId,
+ oldSession,
+ route,
+ sessionHints,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// Binder call
@@ -580,9 +593,20 @@
// Binder call
@Override
- public void requestCreateSessionWithManager(IMediaRouter2Manager manager,
- int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
- mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
+ public void requestCreateSessionWithManager(
+ IMediaRouter2Manager manager,
+ int requestId,
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ UserHandle transferInitiatorUserHandle,
+ String transferInitiatorPackageName) {
+ mService2.requestCreateSessionWithManager(
+ manager,
+ requestId,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// Binder call
@@ -601,9 +625,20 @@
// Binder call
@Override
- public void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId,
- String sessionId, MediaRoute2Info route) {
- mService2.transferToRouteWithManager(manager, requestId, sessionId, route);
+ public void transferToRouteWithManager(
+ IMediaRouter2Manager manager,
+ int requestId,
+ String sessionId,
+ MediaRoute2Info route,
+ UserHandle transferInitiatorUserHandle,
+ String transferInitiatorPackageName) {
+ mService2.transferToRouteWithManager(
+ manager,
+ requestId,
+ sessionId,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 9d151c2..f7210dd 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -26,6 +27,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.MediaRouter2Utils;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -39,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.media.flags.Flags;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -79,6 +82,10 @@
@GuardedBy("mRequestLock")
private volatile SessionCreationRequest mPendingSessionCreationRequest;
+ private final Object mTransferLock = new Object();
+ @GuardedBy("mTransferLock")
+ @Nullable private volatile SessionCreationRequest mPendingTransferRequest;
+
SystemMediaRoute2Provider(Context context, UserHandle user) {
super(COMPONENT_NAME);
mIsSystemRouteProvider = true;
@@ -146,17 +153,30 @@
}
@Override
- public void requestCreateSession(long requestId, String packageName, String routeId,
- Bundle sessionHints) {
+ public void requestCreateSession(
+ long requestId,
+ String packageName,
+ String routeId,
+ Bundle sessionHints,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
// Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
// a route ID different from the default route ID. The service should've filtered.
if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
return;
}
- if (TextUtils.equals(routeId, mSelectedRouteId)) {
- mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
- return;
+
+ if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ if (TextUtils.equals(routeId, mSelectedRouteId)) {
+ RoutingSessionInfo currentSessionInfo;
+ synchronized (mLock) {
+ currentSessionInfo = mSessionInfos.get(0);
+ }
+ mCallback.onSessionCreated(this, requestId, currentSessionInfo);
+ return;
+ }
}
synchronized (mRequestLock) {
@@ -165,10 +185,23 @@
mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId,
MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
}
- mPendingSessionCreationRequest = new SessionCreationRequest(requestId, routeId);
+ mPendingSessionCreationRequest =
+ new SessionCreationRequest(
+ requestId,
+ routeId,
+ RoutingSessionInfo.TRANSFER_REASON_FALLBACK,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
- transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
+ // Only unprivileged routers call this method, therefore we use TRANSFER_REASON_APP.
+ transferToRoute(
+ requestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ SYSTEM_SESSION_ID,
+ routeId,
+ transferReason);
}
@Override
@@ -193,12 +226,31 @@
}
@Override
- public void transferToRoute(long requestId, String sessionId, String routeId) {
+ public void transferToRoute(
+ long requestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ String sessionId,
+ String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason) {
if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
// The currently selected route is the default route.
Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
return;
}
+
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ synchronized (mTransferLock) {
+ mPendingTransferRequest =
+ new SessionCreationRequest(
+ requestId,
+ routeId,
+ transferReason,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
+ }
+ }
+
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
boolean isAvailableDeviceRoute =
mDeviceRouteController.getAvailableRoutes().stream()
@@ -218,6 +270,11 @@
mDeviceRouteController.transferTo(null);
mBluetoothRouteController.transferTo(routeId);
}
+
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
+ && updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
}
@Override
@@ -322,9 +379,11 @@
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
MediaRoute2Info selectedRoute = selectedDeviceRoute;
MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute();
+ List<String> transferableRoutes = new ArrayList<>();
+
if (selectedBtRoute != null) {
selectedRoute = selectedBtRoute;
- builder.addTransferableRoute(selectedDeviceRoute.getId());
+ transferableRoutes.add(selectedDeviceRoute.getId());
}
mSelectedRouteId = selectedRoute.getId();
mDefaultRoute =
@@ -337,12 +396,54 @@
for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
String routeId = route.getId();
if (!mSelectedRouteId.equals(routeId)) {
- builder.addTransferableRoute(routeId);
+ transferableRoutes.add(routeId);
}
}
}
for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) {
- builder.addTransferableRoute(route.getId());
+ transferableRoutes.add(route.getId());
+ }
+
+ for (String route : transferableRoutes) {
+ builder.addTransferableRoute(route);
+ }
+
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ int transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK;
+ UserHandle transferInitiatorUserHandle = null;
+ String transferInitiatorPackageName = null;
+
+ if (oldSessionInfo != null
+ && containsSelectedRouteWithId(oldSessionInfo, selectedRoute.getId())) {
+ transferReason = oldSessionInfo.getTransferReason();
+ transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle();
+ transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName();
+ }
+
+ synchronized (mTransferLock) {
+ if (mPendingTransferRequest != null) {
+ boolean isTransferringToTheSelectedRoute =
+ mPendingTransferRequest.isTargetRoute(selectedRoute);
+ boolean canBePotentiallyTransferred =
+ mPendingTransferRequest.isInsideOfRoutesList(transferableRoutes);
+
+ if (isTransferringToTheSelectedRoute) {
+ transferReason = mPendingTransferRequest.mTransferReason;
+ transferInitiatorUserHandle =
+ mPendingTransferRequest.mTransferInitiatorUserHandle;
+ transferInitiatorPackageName =
+ mPendingTransferRequest.mTransferInitiatorPackageName;
+
+ mPendingTransferRequest = null;
+ } else if (!canBePotentiallyTransferred) {
+ mPendingTransferRequest = null;
+ }
+ }
+ }
+
+ builder.setTransferReason(transferReason)
+ .setTransferInitiator(
+ transferInitiatorUserHandle, transferInitiatorPackageName);
}
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
@@ -424,6 +525,22 @@
return false;
}
+ private boolean containsSelectedRouteWithId(
+ @Nullable RoutingSessionInfo sessionInfo, @NonNull String selectedRouteId) {
+ if (sessionInfo == null) {
+ return false;
+ }
+
+ List<String> selectedRoutes = sessionInfo.getSelectedRoutes();
+
+ if (selectedRoutes.size() != 1) {
+ throw new IllegalStateException("Selected routes list should contain only 1 route id.");
+ }
+
+ String oldSelectedRouteId = MediaRouter2Utils.getOriginalId(selectedRoutes.get(0));
+ return oldSelectedRouteId != null && oldSelectedRouteId.equals(selectedRouteId);
+ }
+
void publishProviderState() {
updateProviderState();
notifyProviderState();
@@ -452,12 +569,47 @@
}
private static class SessionCreationRequest {
- final long mRequestId;
- final String mRouteId;
+ private final long mRequestId;
+ @NonNull private final String mRouteId;
- SessionCreationRequest(long requestId, String routeId) {
- this.mRequestId = requestId;
- this.mRouteId = routeId;
+ @RoutingSessionInfo.TransferReason private final int mTransferReason;
+
+ @NonNull private final UserHandle mTransferInitiatorUserHandle;
+ @NonNull private final String mTransferInitiatorPackageName;
+
+ SessionCreationRequest(
+ long requestId,
+ @NonNull String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
+ mRequestId = requestId;
+ mRouteId = routeId;
+ mTransferReason = transferReason;
+ mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+ mTransferInitiatorPackageName = transferInitiatorPackageName;
+ }
+
+ private boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
+ if (route2Info == null) {
+ return false;
+ }
+
+ return isTargetRoute(route2Info.getId());
+ }
+
+ private boolean isTargetRoute(@Nullable String routeId) {
+ return mRouteId.equals(routeId);
+ }
+
+ private boolean isInsideOfRoutesList(@NonNull List<String> routesList) {
+ for (String routeId : routesList) {
+ if (isTargetRoute(routeId)) {
+ return true;
+ }
+ }
+
+ return false;
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 135a467..f49d51c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5617,7 +5617,8 @@
return !isCompatChangeEnabled
|| isCallerSystemOrSystemUi()
|| hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
- AssociationRequest.DEVICE_PROFILE_WATCH);
+ Set.of(AssociationRequest.DEVICE_PROFILE_WATCH,
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION));
}
private void enforcePolicyAccess(String pkg, String method) {
@@ -10800,7 +10801,7 @@
}
private boolean hasCompanionDevice(String pkg, @UserIdInt int userId,
- @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) {
+ @Nullable Set</* @AssociationRequest.DeviceProfile */ String> withDeviceProfiles) {
if (mCompanionManager == null) {
mCompanionManager = getCompanionManager();
}
@@ -10812,7 +10813,7 @@
try {
List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId);
for (AssociationInfo association : associations) {
- if (withDeviceProfile == null || withDeviceProfile.equals(
+ if (withDeviceProfiles == null || withDeviceProfiles.contains(
association.getDeviceProfile())) {
return true;
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 55d8a0f..8e79922a 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -41,3 +41,12 @@
description: "This flag controls the fix for notifications autogroup summary icon updates"
bug: "227693160"
}
+
+flag {
+ name: "sensitive_notification_app_protection"
+ namespace: "systemui"
+ description: "This flag controls the sensitive notification app protections while screen sharing"
+ bug: "312784351"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 3b9f9c8..41d0176 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -46,11 +46,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.pm.parsing.PackageCacher;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.utils.WatchedArrayMap;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 65bfb2f..b638d30 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -146,6 +146,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -154,6 +155,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
@@ -178,7 +180,6 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.parsing.PackageCacher;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -672,6 +673,9 @@
if (pkgSetting == null || pkgSetting.getPkg() == null) {
return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
}
+ if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) {
+ return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
+ }
if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
// only allow the existing package to be used if it's installed as a full
// application for at least one user
@@ -1167,8 +1171,9 @@
parseFlags);
archivedPackage = request.getPackageLite().getArchivedPackage();
}
- } catch (PackageManagerException | PackageParserException e) {
- throw new PrepareFailure("Failed parse during installPackageLI", e);
+ } catch (PackageParserException e) {
+ throw new PrepareFailure(e.error,
+ ExceptionUtils.getCompleteMessage("Failed parse during installPackageLI", e));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2858,14 +2863,17 @@
mPm.notifyPackageChanged(packageName, request.getAppId());
}
- for (int userId : firstUserIds) {
- // Apply restricted settings on potentially dangerous packages. Needs to happen
- // after appOpsManager is notified of the new package
- if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
- || request.getPackageSource()
- == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
- enableRestrictedSettings(packageName, request.getAppId(), userId);
- }
+ // Apply restricted settings on potentially dangerous packages. Needs to happen
+ // after appOpsManager is notified of the new package
+ if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+ || request.getPackageSource()
+ == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
+ final int appId = request.getAppId();
+ mPm.mHandler.post(() -> {
+ for (int userId : firstUserIds) {
+ enableRestrictedSettings(packageName, appId, userId);
+ }
+ });
}
// Log current value of "unknown sources" setting
@@ -3680,6 +3688,8 @@
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
+ } catch (PackageParserException e) {
+ throw new PackageManagerException(e.error, e.getMessage(), e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 187cada..e970d2c 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -51,8 +51,8 @@
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.util.Preconditions;
-import com.android.server.pm.parsing.PackageParser2;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2942bbb..cbd65a4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -111,6 +111,7 @@
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.util.ImageUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.TypedXmlPullParser;
@@ -120,7 +121,6 @@
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.utils.RequestThrottle;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1d5b8c3..81d5d81 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -181,6 +181,7 @@
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
@@ -221,8 +222,8 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
import com.android.server.pm.local.PackageManagerLocalImpl;
+import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerService;
@@ -1698,7 +1699,7 @@
() -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(),
- pm.mCacheDir,
+ new PackageCacher(pm.mCacheDir),
pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,
pm.mPackageParserCallback) /* scanningPackageParserProducer */,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index ebf1c04..049737d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -27,12 +27,12 @@
import android.util.DisplayMetrics;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.SystemConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.resolution.ComponentResolver;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 655b9c9..86d78dc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -31,10 +31,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 15d2fdc..1fe49c7 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -41,10 +41,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.InstallLocationUtils;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.rollback.RollbackManagerInternal;
import java.io.File;
@@ -399,7 +400,7 @@
try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
File apexFile = new File(apexInfo.modulePath);
parsedPackage = packageParser.parsePackage(apexFile, 0, false);
- } catch (PackageManagerException e) {
+ } catch (PackageParserException e) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 1089ac9..0511639 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -22,9 +22,10 @@
import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ConcurrentUtils;
-import com.android.server.pm.parsing.PackageParser2;
import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
@@ -125,6 +126,10 @@
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageManagerException {
- return mPackageParser.parsePackage(scanFile, parseFlags, true);
+ try {
+ return mPackageParser.parsePackage(scanFile, parseFlags, true);
+ } catch (PackageParserException e) {
+ throw new PackageManagerException(e.error, e.getMessage(), e);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 79c9c8e..b6267c4 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -28,6 +28,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.IPackageCacher;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.ApexManager;
@@ -39,7 +40,7 @@
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
-public class PackageCacher {
+public class PackageCacher implements IPackageCacher {
private static final String TAG = "PackageCacher";
@@ -162,6 +163,7 @@
* Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
* or {@code null} if no cached result exists.
*/
+ @Override
public ParsedPackage getCachedResult(File packageFile, int flags) {
final String cacheKey = getCacheKey(packageFile, flags);
final File cacheFile = new File(mCacheDir, cacheKey);
@@ -192,6 +194,7 @@
/**
* Caches the parse result for {@code packageFile} with flags {@code flags}.
*/
+ @Override
public void cacheResult(File packageFile, int flags, ParsedPackage parsed) {
try {
final String cacheKey = getCacheKey(packageFile, flags);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParserUtils.java b/services/core/java/com/android/server/pm/parsing/PackageParserUtils.java
new file mode 100644
index 0000000..03a7a37
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/PackageParserUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.PackageManagerService;
+
+import java.util.Set;
+
+public class PackageParserUtils {
+ /**
+ * For parsing inside the system server but outside of {@link PackageManagerService}.
+ * Generally used for parsing information in an APK that hasn't been installed yet.
+ *
+ * This must be called inside the system process as it relies on {@link ServiceManager}.
+ */
+ @NonNull
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public static PackageParser2 forParsingFileWithDefaults() {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ return new PackageParser2(null /* separateProcesses */, null /* displayMetrics */,
+ null /* cacheDir */, new PackageParser2.Callback() {
+ @Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
+ try {
+ return platformCompat.isChangeEnabled(changeId, appInfo);
+ } catch (Exception e) {
+ // This shouldn't happen, but assume enforcement if it does
+ Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e);
+ return true;
+ }
+ }
+
+ @Override
+ public boolean hasFeature(String feature) {
+ // Assume the device doesn't support anything. This will affect permission parsing
+ // and will force <uses-permission/> declarations to include all requiredNotFeature
+ // permissions and exclude all requiredFeature permissions. This mirrors the old
+ // behavior.
+ return false;
+ }
+
+ @Override
+ public Set<String> getHiddenApiWhitelistedApps() {
+ return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
+ }
+
+ @Override
+ public Set<String> getInstallConstraintsAllowlist() {
+ return SystemConfig.getInstance().getInstallConstraintsAllowlist();
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
index b531b0e..611e4ed 100644
--- a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
+++ b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
@@ -76,6 +76,18 @@
getActionsBufferWithLazyCleanUp(keyCode, downTime).setExecutable();
}
+ /**
+ * Clears all the queued action for given key code.
+ *
+ * @param keyCode the key code whose queued actions will be cleared.
+ */
+ public void cancelQueuedAction(int keyCode) {
+ TimedActionsBuffer actionsBuffer = mBuffers.get(keyCode);
+ if (actionsBuffer != null) {
+ actionsBuffer.clear();
+ }
+ }
+
private TimedActionsBuffer getActionsBufferWithLazyCleanUp(int keyCode, long downTime) {
TimedActionsBuffer buffer = mBuffers.get(keyCode);
if (buffer == null || buffer.getDownTime() != downTime) {
@@ -146,6 +158,10 @@
mActions.clear();
}
+ void clear() {
+ mActions.clear();
+ }
+
void dump(String prefix, PrintWriter pw) {
if (mExecutable) {
pw.println(prefix + " " + KeyEvent.keyCodeToString(mKeyCode) + ": executable");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e8b54d58..3000a1c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -103,6 +103,7 @@
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IUiModeManager;
@@ -584,6 +585,10 @@
private int mLongPressOnStemPrimaryBehavior;
private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
+ // The focused task at the time when the first STEM_PRIMARY key was released. This can only
+ // be accessed from the looper thread.
+ private RootTaskInfo mFocusedTaskInfoOnStemPrimarySingleKeyUp;
+
private boolean mHandleVolumeKeysInWM;
private boolean mPendingKeyguardOccluded;
@@ -2135,12 +2140,10 @@
static class Injector {
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
- private final Looper mLooper;
- Injector(Context context, WindowManagerFuncs funcs, Looper looper) {
+ Injector(Context context, WindowManagerFuncs funcs) {
mContext = context;
mWindowManagerFuncs = funcs;
- mLooper = looper;
}
Context getContext() {
@@ -2152,7 +2155,7 @@
}
Looper getLooper() {
- return mLooper;
+ return Looper.myLooper();
}
AccessibilityShortcutController getAccessibilityShortcutController(
@@ -2195,7 +2198,7 @@
/** {@inheritDoc} */
@Override
public void init(Context context, WindowManagerFuncs funcs) {
- init(new Injector(context, funcs, Looper.myLooper()));
+ init(new Injector(context, funcs));
}
@VisibleForTesting
@@ -2723,7 +2726,7 @@
@Override
void onPress(long downTime, int unusedDisplayId) {
- if (mShouldEarlyShortPressOnStemPrimary) {
+ if (shouldHandleStemPrimaryEarlyShortPress()) {
return;
}
// Short-press should be triggered only if app doesn't handle it.
@@ -2747,6 +2750,11 @@
if (count == 3
&& mTriplePressOnStemPrimaryBehavior
== TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) {
+ // Cancel any queued actions for current key code to prevent them from being
+ // launched after a11y layer enabled. If the action happens early,
+ // undoEarlySinglePress will make sure the correct task is on top.
+ mDeferredKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
+ undoEarlySinglePress();
stemPrimaryPress(count);
} else {
// Other multi-press gestures should be triggered only if app doesn't handle it.
@@ -2755,6 +2763,27 @@
}
}
+ /**
+ * This method undo the previously launched early-single-press action by bringing the
+ * focused task before launching early-single-press back to top.
+ */
+ private void undoEarlySinglePress() {
+ if (shouldHandleStemPrimaryEarlyShortPress()
+ && mFocusedTaskInfoOnStemPrimarySingleKeyUp != null) {
+ try {
+ mActivityManagerService.startActivityFromRecents(
+ mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId, null);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(
+ TAG,
+ "Failed to start task "
+ + mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId
+ + " from recents",
+ e);
+ }
+ }
+ }
+
@Override
void onKeyUp(long eventTime, int count, int unusedDisplayId) {
if (count == 1) {
@@ -2763,15 +2792,49 @@
// It is possible that we may navigate away from this task before the double
// press is detected, as a result of the first press, so we save the current
// most recent task before that happens.
+ // TODO(b/311497918): guard this with DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP
mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp =
mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
- if (mShouldEarlyShortPressOnStemPrimary) {
+
+ mFocusedTaskInfoOnStemPrimarySingleKeyUp = null;
+
+ if (shouldHandleStemPrimaryEarlyShortPress()) {
// Key-up gesture should be triggered only if app doesn't handle it.
mDeferredKeyActionExecutor.queueKeyAction(
- KeyEvent.KEYCODE_STEM_PRIMARY, eventTime, () -> stemPrimaryPress(1));
+ KeyEvent.KEYCODE_STEM_PRIMARY,
+ eventTime,
+ () -> {
+ // Save the info of the focused task on screen. This may be used
+ // later to bring the current focused task back to top. For
+ // example, stem primary triple press enables the A11y interface
+ // on top of the current focused task. When early single press is
+ // enabled for stem primary, the focused task could change to
+ // something else upon first key up event. In that case, we will
+ // bring the task recorded by this variable back to top. Then, start
+ // A11y interface.
+ try {
+ mFocusedTaskInfoOnStemPrimarySingleKeyUp =
+ mActivityManagerService.getFocusedRootTaskInfo();
+ } catch (RemoteException e) {
+ Slog.e(
+ TAG,
+ "StemPrimaryKeyRule: onKeyUp: error while getting "
+ + "focused task "
+ + "info.",
+ e);
+ }
+
+ stemPrimaryPress(1);
+ });
}
}
}
+
+ // TODO(b/311497918): make a shouldHandlePowerEarlyShortPress for power button.
+ private boolean shouldHandleStemPrimaryEarlyShortPress() {
+ return mShouldEarlyShortPressOnStemPrimary
+ && mShortPressOnStemPrimaryBehavior == SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
+ }
}
private void initSingleKeyGestureRules(Looper looper) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1ce87a7..145eb3b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -125,6 +125,7 @@
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -683,6 +684,7 @@
private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
new WindowState.UpdateReportedVisibilityResults();
+ // TODO(b/317000737): Replace it with visibility states lookup.
int mTransitionChangeFlags;
/** Whether we need to setup the animation to animate only within the letterbox. */
@@ -5468,8 +5470,13 @@
// Defer committing visibility until transition starts.
if (isCollecting) {
// It may be occluded by the activity above that calls convertFromTranslucent().
- if (!visible && mTransitionController.inPlayingTransition(this)) {
- mTransitionChangeFlags |= FLAG_IS_OCCLUDED;
+ // Or it may be restoring transient launch to invisible when finishing transition.
+ if (!visible) {
+ if (mTransitionController.inPlayingTransition(this)) {
+ mTransitionChangeFlags |= FLAG_IS_OCCLUDED;
+ } else if (mTransitionController.inFinishingTransition(this)) {
+ mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
+ }
}
return;
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4929df80..eed46fe 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -36,6 +36,7 @@
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -152,36 +153,25 @@
static final int BAL_ALLOW_SDK_SANDBOX = 10;
static String balCodeToString(@BalCode int balCode) {
- switch (balCode) {
- case BAL_ALLOW_ALLOWLISTED_COMPONENT:
- return "BAL_ALLOW_ALLOWLISTED_COMPONENT";
- case BAL_ALLOW_ALLOWLISTED_UID:
- return "BAL_ALLOW_ALLOWLISTED_UID";
- case BAL_ALLOW_DEFAULT:
- return "BAL_ALLOW_DEFAULT";
- case BAL_ALLOW_FOREGROUND:
- return "BAL_ALLOW_FOREGROUND";
- case BAL_ALLOW_GRACE_PERIOD:
- return "BAL_ALLOW_GRACE_PERIOD";
- case BAL_ALLOW_PENDING_INTENT:
- return "BAL_ALLOW_PENDING_INTENT";
- case BAL_ALLOW_PERMISSION:
- return "BAL_ALLOW_PERMISSION";
- case BAL_ALLOW_SAW_PERMISSION:
- return "BAL_ALLOW_SAW_PERMISSION";
- case BAL_ALLOW_SDK_SANDBOX:
- return "BAL_ALLOW_SDK_SANDBOX";
- case BAL_ALLOW_VISIBLE_WINDOW:
- return "BAL_ALLOW_VISIBLE_WINDOW";
- case BAL_BLOCK:
- return "BAL_BLOCK";
- default:
- throw new IllegalArgumentException("Unexpected value: " + balCode);
- }
+ return switch (balCode) {
+ case BAL_ALLOW_ALLOWLISTED_COMPONENT -> "BAL_ALLOW_ALLOWLISTED_COMPONENT";
+ case BAL_ALLOW_ALLOWLISTED_UID -> "BAL_ALLOW_ALLOWLISTED_UID";
+ case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT";
+ case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND";
+ case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD";
+ case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT";
+ case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION";
+ case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION";
+ case BAL_ALLOW_SDK_SANDBOX -> "BAL_ALLOW_SDK_SANDBOX";
+ case BAL_ALLOW_VISIBLE_WINDOW -> "BAL_ALLOW_VISIBLE_WINDOW";
+ case BAL_BLOCK -> "BAL_BLOCK";
+ default -> throw new IllegalArgumentException("Unexpected value: " + balCode);
+ };
}
@GuardedBy("mService.mGlobalLock")
- private HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity = new HashMap<>();
+ private final HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity =
+ new HashMap<>();
@GuardedBy("mService.mGlobalLock")
private FinishedActivityEntry mTopFinishedActivity = null;
@@ -467,9 +457,8 @@
return !blocks();
}
- BalVerdict setOnlyCreatorAllows(boolean onlyCreatorAllows) {
+ void setOnlyCreatorAllows(boolean onlyCreatorAllows) {
mOnlyCreatorAllows = onlyCreatorAllows;
- return this;
}
boolean onlyCreatorAllows() {
@@ -481,10 +470,6 @@
return this;
}
- private boolean isBasedOnRealCaller() {
- return mBasedOnRealCaller;
- }
-
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(balCodeToString(mCode));
@@ -583,15 +568,14 @@
BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
if (!state.hasRealCaller()) {
- BalVerdict resultForRealCaller = null; // nothing to compute
if (resultForCaller.allows()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed. "
- + state.dump(resultForCaller, resultForRealCaller));
+ + state.dump(resultForCaller, resultForCaller));
}
return statsLog(resultForCaller, state);
}
- return abortLaunch(state, resultForCaller, resultForRealCaller);
+ return abortLaunch(state, resultForCaller, resultForCaller);
}
// The realCaller result is only calculated for PendingIntents (indicated by a valid
@@ -653,7 +637,7 @@
+ " if the PI creator upgrades target_sdk to 35+"
+ " AND the PI sender upgrades target_sdk to 34+! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
+ showBalRiskToast();
// return the realCaller result for backwards compatibility
return statsLog(resultForRealCaller, state);
}
@@ -679,7 +663,7 @@
+ " if the PI creator upgrades target_sdk to 35+! "
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
+ showBalRiskToast();
return statsLog(resultForCaller, state);
}
Slog.wtf(TAG,
@@ -696,7 +680,7 @@
+ " if the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
+ showBalRiskToast();
return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
@@ -712,7 +696,7 @@
BalVerdict resultForRealCaller) {
Slog.w(TAG, "Background activity launch blocked! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalBlockedToast("BAL blocked", state);
+ showBalBlockedToast();
return statsLog(BalVerdict.BLOCK, state);
}
@@ -910,7 +894,7 @@
/**
* Check if the app allows BAL.
- *
+ * <p>
* See {@link BackgroundLaunchProcessController#areBackgroundActivityStartsAllowed(int, int,
* String, int, boolean, boolean, boolean, long, long, long)} for details on the
* exceptions.
@@ -1104,19 +1088,15 @@
return true;
}
- private void showBalBlockedToast(String toastText, BalState state) {
+ private void showBalBlockedToast() {
if (balShowToastsBlocked()) {
- showToast(toastText
- + " caller:" + state.mCallingPackage
- + " realCaller:" + state.mRealCallingPackage);
+ showToast("BAL blocked. go/debug-bal");
}
}
- private void showBalRiskToast(String toastText, BalState state) {
+ private void showBalRiskToast() {
if (balShowToasts()) {
- showToast(toastText
- + " caller:" + state.mCallingPackage
- + " realCaller:" + state.mRealCallingPackage);
+ showToast("BAL allowed in compat mode. go/debug-bal");
}
}
@@ -1281,7 +1261,7 @@
* 2. Or top of an adjacent task fragment to (1)
* <p>
* The 'sourceRecord' can be considered top even if it is 'finishing'
- *
+ * <p>
* Returns a class where the elements are:
* <pre>
* shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
@@ -1344,7 +1324,7 @@
/**
* Determines if a source is allowed to add or remove activities from the task,
* if the current ActivityRecord is above it in the stack
- *
+ * <p>
* A transition is blocked ({@code false} returned) if all of the following are met:
* <pre>
* 1. The source activity and the current activity record belong to different apps
@@ -1489,8 +1469,8 @@
if (code == BAL_ALLOW_PENDING_INTENT
&& (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
- String activityName =
- intent != null ? intent.getComponent().flattenToShortString() : "";
+ String activityName = intent != null
+ ? requireNonNull(intent.getComponent()).flattenToShortString() : "";
FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
activityName,
BAL_ALLOW_PENDING_INTENT,
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f020bfa..3117db5 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -43,6 +43,7 @@
import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
+import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
@@ -3067,6 +3068,10 @@
Slog.e(TAG, "Unexpected launch-task-behind operation in shell transition");
flags |= FLAG_TASK_LAUNCHING_BEHIND;
}
+ if ((topActivity.mTransitionChangeFlags & FLAGS_IS_OCCLUDED_NO_ANIMATION)
+ == FLAGS_IS_OCCLUDED_NO_ANIMATION) {
+ flags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
+ }
}
if (task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9c21e4c..ec4bdf9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -202,7 +202,6 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
-import android.os.PowerManager.WakeReason;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -702,11 +701,6 @@
*/
private final Region mTapExcludeRegion = new Region();
- /**
- * Used for testing because the real PowerManager is final.
- */
- private PowerManagerWrapper mPowerManagerWrapper;
-
private static final StringBuilder sTmpSB = new StringBuilder();
/**
@@ -1061,34 +1055,9 @@
return mOnBackInvokedCallbackInfo;
}
- interface PowerManagerWrapper {
- void wakeUp(long time, @WakeReason int reason, String details);
-
- boolean isInteractive();
-
- }
-
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
- this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
- ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
- @Override
- public void wakeUp(long time, @WakeReason int reason, String details) {
- service.mPowerManager.wakeUp(time, reason, details);
- }
-
- @Override
- public boolean isInteractive() {
- return service.mPowerManager.isInteractive();
- }
- });
- }
-
- WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
- int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
- PowerManagerWrapper powerManagerWrapper) {
super(service);
mTmpTransaction = service.mTransactionFactory.get();
mSession = s;
@@ -1106,7 +1075,6 @@
mViewVisibility = viewVisibility;
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
- mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
mActivityRecord != null
@@ -2831,12 +2799,12 @@
boolean canTurnScreenOn = mActivityRecord == null || mActivityRecord.currentLaunchCanTurnScreenOn();
if (allowTheaterMode && canTurnScreenOn
- && (mWmService.mAtmService.isDreaming()
- || !mPowerManagerWrapper.isInteractive())) {
+ && (mWmService.mAtmService.isDreaming()
+ || !mWmService.mPowerManager.isInteractive())) {
if (DEBUG_VISIBILITY || DEBUG_POWER) {
Slog.v(TAG, "Relayout window turning screen on: " + this);
}
- mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
+ mWmService.mPowerManager.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
}
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 061fe0f..cc08488 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -31,5 +31,5 @@
per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
-# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
-per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
+# Bug component : 158088 = per-file *AnrTimer*
+per-file *AnrTimer* = file:/PERFORMANCE_OWNERS
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 8078745..3cbceec 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -632,7 +632,7 @@
<xs:annotation name="final"/>
</xs:element>
<xs:element name="mode" type="AutoBrightnessModeName" minOccurs="0"/>
- <xs:element name="setting" type="xs:string" minOccurs="0"/>
+ <xs:element name="setting" type="AutoBrightnessSettingName" minOccurs="0"/>
</xs:complexType>
<!-- Represents a point in the display brightness mapping, representing the lux level from the
@@ -775,4 +775,13 @@
<xs:enumeration value="doze"/>
</xs:restriction>
</xs:simpleType>
+
+ <!-- Predefined auto-brighntess settings -->
+ <xs:simpleType name="AutoBrightnessSettingName">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="dim"/>
+ <xs:enumeration value="normal"/>
+ <xs:enumeration value="bright"/>
+ </xs:restriction>
+ </xs:simpleType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 91172a3..79ea274 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -23,6 +23,13 @@
enum_constant public static final com.android.server.display.config.AutoBrightnessModeName idle;
}
+ public enum AutoBrightnessSettingName {
+ method public String getRawName();
+ enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName bright;
+ enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName dim;
+ enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName normal;
+ }
+
public class BlockingZoneConfig {
ctor public BlockingZoneConfig();
method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold();
@@ -227,10 +234,10 @@
ctor public LuxToBrightnessMapping();
method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap();
method public com.android.server.display.config.AutoBrightnessModeName getMode();
- method public String getSetting();
+ method public com.android.server.display.config.AutoBrightnessSettingName getSetting();
method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
method public void setMode(com.android.server.display.config.AutoBrightnessModeName);
- method public void setSetting(String);
+ method public void setSetting(com.android.server.display.config.AutoBrightnessSettingName);
}
public class NitsMap {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 03e45a2..71f5c75 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -58,6 +58,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.permission.CompatibilityPermissionInfo;
@@ -84,7 +86,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.PackageParserUtils;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
@@ -185,7 +187,7 @@
@Test
public void test_serializePackage() throws Exception {
- try (PackageParser2 pp = PackageParser2.forParsingFileWithDefaults()) {
+ try (PackageParser2 pp = PackageParserUtils.forParsingFileWithDefaults()) {
AndroidPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
true /* useCaches */).hideAsFinal();
@@ -363,7 +365,7 @@
actualDisplayCategory = activity.getRequiredDisplayCategory();
}
}
- } catch (PackageManagerException e) {
+ } catch (PackageParserException e) {
assertThat(e.getMessage()).contains(
"requiredDisplayCategory attribute can only consist"
+ " of alphanumeric characters, '_', and '.'");
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
index 8a74e24..3761240 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -21,8 +21,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.TestPackageParser2;
import junit.framework.Assert;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index b63950c..a28b28a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -38,6 +38,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivityUtils;
import com.android.internal.pm.pkg.component.ParsedComponent;
@@ -45,7 +46,6 @@
import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.PackageManagerException;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.test.service.server.R;
@@ -608,7 +608,7 @@
try {
parsePackage(filename, resId, x -> x);
expect.withMessage("Expected parsing error %s from %s", result, filename).fail();
- } catch (PackageManagerException expected) {
+ } catch (PackageParserException expected) {
expect.that(expected.error).isEqualTo(result);
}
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 98af63c..1d668cd 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -18,8 +18,8 @@
import android.content.pm.PackageManager
import android.platform.test.annotations.Postsubmit
+import com.android.internal.pm.parsing.PackageParserException
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
-import com.android.server.pm.PackageManagerException
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageManagerServiceUtils
import java.io.File
@@ -39,7 +39,7 @@
@Postsubmit
class SystemPartitionParseTest {
- private val parser = PackageParser2.forParsingFileWithDefaults()
+ private val parser = PackageParserUtils.forParsingFileWithDefaults()
@get:Rule
val tempFolder = TemporaryFolder()
@@ -86,7 +86,7 @@
}
}
.mapNotNull { it.exceptionOrNull() }
- .filterNot { (it as? PackageManagerException)?.error ==
+ .filterNot { (it as? PackageParserException)?.error ==
PackageManager.INSTALL_PARSE_FAILED_SKIPPED }
if (exceptions.isEmpty()) return
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index f4eaa5b..fb73aff 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import static org.junit.Assert.assertEquals;
@@ -29,21 +30,22 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
import android.os.PowerManager;
+import android.provider.Settings;
+import android.testing.TestableContext;
import android.util.MathUtils;
import android.util.Spline;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
-
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import java.util.Arrays;
@@ -154,15 +156,23 @@
private static final float TOLERANCE = 0.0001f;
- @Mock
- DisplayWhiteBalanceController mMockDwbc;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Before
+ public void setUp() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL);
+ }
@Test
public void testSimpleStrategyMappingAtControlPoints() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 0; i < LUX_LEVELS.length; i++) {
assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), TOLERANCE);
@@ -171,10 +181,10 @@
@Test
public void testSimpleStrategyMappingBetweenControlPoints() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 1; i < LUX_LEVELS.length; i++) {
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
@@ -186,10 +196,10 @@
@Test
public void testSimpleStrategyIgnoresNewConfiguration() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
final float[] lux = { 0f, 1f };
final float[] nits = { 0, PowerManager.BRIGHTNESS_ON };
@@ -202,10 +212,10 @@
@Test
public void testSimpleStrategyIgnoresNullConfiguration() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
strategy.setBrightnessConfiguration(null);
final int n = DISPLAY_LEVELS.length;
@@ -216,11 +226,11 @@
@Test
public void testPhysicalStrategyMappingAtControlPoints() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
.build();
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1],
@@ -235,11 +245,11 @@
@Test
public void testPhysicalStrategyMappingBetweenControlPoints() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
Spline brightnessToNits =
Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS);
@@ -254,11 +264,11 @@
@Test
public void testPhysicalStrategyUsesNewConfigurations() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
.build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
final float[] lux = {0f, 1f};
final float[] nits = {
@@ -281,11 +291,11 @@
@Test
public void testPhysicalStrategyRecalculateSplines() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
.build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
@@ -326,12 +336,12 @@
@Test
public void testDefaultStrategyIsPhysical() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
.build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
}
@@ -342,19 +352,19 @@
float tmp = lux[idx];
lux[idx] = lux[idx + 1];
lux[idx + 1] = tmp;
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNull(strategy);
// And make sure we get the same result even if it's monotone but not increasing.
lux[idx] = lux[idx + 1];
ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
}
@@ -365,74 +375,74 @@
// Make sure it's strictly increasing so that the only failure is the differing array
// lengths
lux[lux.length - 1] = lux[lux.length - 2] + 1;
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNull(strategy);
ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
.setAutoBrightnessLevels(DISPLAY_LEVELS).build();
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
// Extra backlight level
final float[] backlight = Arrays.copyOf(DISPLAY_LEVELS, DISPLAY_LEVELS.length + 1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
- res = createResources();
+ setUpResources();
ddc = new DdcBuilder().setAutoBrightnessLevels(backlight).build();
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
// Extra nits level
final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length + 1);
nits[nits.length - 1] = nits[nits.length - 2] + 1;
- res = createResources();
+ setUpResources();
ddc = new DdcBuilder().setAutoBrightnessLevelsNits(nits)
.setAutoBrightnessLevels(EMPTY_FLOAT_ARRAY).build();
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
}
@Test
public void testPhysicalStrategyRequiresNitsMapping() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setNitsRange(EMPTY_FLOAT_ARRAY).build();
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNull(physical);
}
@Test
public void testStrategiesAdaptToUserDataPoint() {
- Resources res = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
.setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
- assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc));
+ assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null));
ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
.setAutoBrightnessLevels(DISPLAY_LEVELS).build();
- res = createResources();
- assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc));
+ setUpResources();
+ assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null));
}
@Test
public void testIdleModeConfigLoadsCorrectly() {
- Resources res = createResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
+ setUpResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
.build();
// Create an idle mode bms
// This will fail if it tries to fetch the wrong configuration.
- BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(res, ddc,
+ BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(mContext, ddc,
AUTO_BRIGHTNESS_MODE_IDLE,
- mMockDwbc);
+ /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", bms);
// Ensure that the config is the one we set
@@ -500,36 +510,31 @@
assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/);
}
- private Resources createResources() {
- return createResources(EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
+ private void setUpResources() {
+ setUpResources(EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
}
- private Resources createResources(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
-
- Resources mockResources = mock(Resources.class);
+ private void setUpResources(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
if (luxLevelsIdle.length > 0) {
int[] luxLevelsIdleResource = Arrays.copyOfRange(luxLevelsIdle, 1,
luxLevelsIdle.length);
- when(mockResources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevelsIdle))
- .thenReturn(luxLevelsIdleResource);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_autoBrightnessLevelsIdle,
+ luxLevelsIdleResource);
}
TypedArray mockBrightnessLevelNitsIdle = createFloatTypedArray(brightnessLevelsNitsIdle);
- when(mockResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle))
- .thenReturn(mockBrightnessLevelNitsIdle);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle,
+ mockBrightnessLevelNitsIdle);
- when(mockResources.getInteger(
- com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
- .thenReturn(1);
- when(mockResources.getInteger(
- com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
- .thenReturn(255);
- when(mockResources.getFraction(
- com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1))
- .thenReturn(MAXIMUM_GAMMA);
- return mockResources;
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.integer.config_screenBrightnessSettingMinimum, 1);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.integer.config_screenBrightnessSettingMaximum, 255);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
+ MAXIMUM_GAMMA);
}
private TypedArray createFloatTypedArray(float[] vals) {
@@ -570,11 +575,11 @@
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
- Resources resources = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
.setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
// Let's start with a validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -600,11 +605,11 @@
final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
- Resources resources = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
.setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
// Validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -627,11 +632,11 @@
public void testGammaCorrectionExtremeChangeAtCenter() {
// Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we
// just make sure the adjustment reflects the change.
- Resources resources = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
.setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f);
strategy.addUserDataPoint(/* lux= */ 2500, /* brightness= */ 1.0f);
assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f);
@@ -650,11 +655,11 @@
final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0);
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
- Resources resources = createResources();
+ setUpResources();
DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
.setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
// Validity, as per tradition:
assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -679,15 +684,33 @@
@Test
public void testGetMode() {
- Resources res = createResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
+ setUpResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
.build();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_IDLE,
- mMockDwbc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_IDLE, /* displayWhiteBalanceController= */ null);
assertEquals(AUTO_BRIGHTNESS_MODE_IDLE, strategy.getMode());
}
+ @Test
+ public void testAutoBrightnessModeAndPreset() {
+ int mode = AUTO_BRIGHTNESS_MODE_DOZE;
+ int preset = Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM;
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, preset);
+
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder()
+ .setAutoBrightnessLevels(mode, preset, DISPLAY_LEVELS)
+ .setAutoBrightnessLevelsLux(mode, preset, LUX_LEVELS).build();
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc, mode,
+ /* displayWhiteBalanceController= */ null);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), TOLERANCE);
+ }
+ }
+
private static class DdcBuilder {
private DisplayDeviceConfig mDdc;
@@ -695,10 +718,11 @@
mDdc = mock(DisplayDeviceConfig.class);
when(mDdc.getNits()).thenReturn(DISPLAY_RANGE_NITS);
when(mDdc.getBrightness()).thenReturn(DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT);
- when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT))
- .thenReturn(LUX_LEVELS);
+ when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)).thenReturn(LUX_LEVELS);
when(mDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
- when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT))
+ when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL))
.thenReturn(EMPTY_FLOAT_ARRAY);
}
@@ -713,8 +737,15 @@
}
DdcBuilder setAutoBrightnessLevelsLux(float[] luxLevels) {
- when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT))
- .thenReturn(luxLevels);
+ when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)).thenReturn(luxLevels);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevelsLux(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset,
+ float[] luxLevels) {
+ when(mDdc.getAutoBrightnessBrighteningLevelsLux(mode, preset)).thenReturn(luxLevels);
return this;
}
@@ -724,7 +755,16 @@
}
DdcBuilder setAutoBrightnessLevels(float[] brightnessLevels) {
- when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT))
+ when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL))
+ .thenReturn(brightnessLevels);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset,
+ float[] brightnessLevels) {
+ when(mDdc.getAutoBrightnessBrighteningLevels(mode, preset))
.thenReturn(brightnessLevels);
return this;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 61c6076..7a84406 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -19,6 +19,7 @@
import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -41,6 +42,7 @@
import android.content.res.TypedArray;
import android.hardware.display.DisplayManagerInternal;
import android.os.Temperature;
+import android.provider.Settings;
import android.util.SparseArray;
import android.util.Spline;
import android.view.SurfaceControl;
@@ -609,10 +611,12 @@
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(),
new float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
- AUTO_BRIGHTNESS_MODE_DEFAULT), new float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+ AUTO_BRIGHTNESS_MODE_DEFAULT, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL),
+ new float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
- AUTO_BRIGHTNESS_MODE_DEFAULT), new float[]{brightnessIntToFloat(50),
- brightnessIntToFloat(100), brightnessIntToFloat(150)}, SMALL_DELTA);
+ AUTO_BRIGHTNESS_MODE_DEFAULT, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL),
+ new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100),
+ brightnessIntToFloat(150)}, SMALL_DELTA);
// Test thresholds
assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA);
@@ -739,31 +743,39 @@
assertArrayEquals(new float[]{0.0f, 80},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
- AUTO_BRIGHTNESS_MODE_DEFAULT), ZERO_DELTA);
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA);
assertArrayEquals(new float[]{0.2f, 0.3f},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
- AUTO_BRIGHTNESS_MODE_DEFAULT), SMALL_DELTA);
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA);
assertArrayEquals(new float[]{0.0f, 90},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux("default", "dim"),
- ZERO_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), ZERO_DELTA);
assertArrayEquals(new float[]{0.3f, 0.4f},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels("default", "dim"),
- SMALL_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA);
assertArrayEquals(new float[]{0.0f, 95},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux("doze", "normal"),
- ZERO_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA);
assertArrayEquals(new float[]{0.35f, 0.45f},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels("doze", "normal"),
- SMALL_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA);
assertArrayEquals(new float[]{0.0f, 100},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux("doze", "bright"),
- ZERO_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), ZERO_DELTA);
assertArrayEquals(new float[]{0.4f, 0.5f},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels("doze", "bright"),
- SMALL_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), SMALL_DELTA);
}
@Test
@@ -776,10 +788,12 @@
assertArrayEquals(new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100),
brightnessIntToFloat(150)},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
- AUTO_BRIGHTNESS_MODE_DEFAULT), SMALL_DELTA);
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA);
assertArrayEquals(new float[]{0, 110, 500},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
- AUTO_BRIGHTNESS_MODE_DEFAULT), ZERO_DELTA);
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA);
assertArrayEquals(new float[]{2, 200, 600},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index ffdc8b4..4cc68cf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -1905,7 +1905,7 @@
}
@Override
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
return mBrightnessMappingStrategy;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 64cdac4..943862f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1669,7 +1669,7 @@
}
@Override
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
return mBrightnessMappingStrategy;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index c2b52b4..57326b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -50,9 +50,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Before;
@@ -175,7 +176,7 @@
mPmService.getPlatformPackage(), /* isUpdatedSystemApp */ false);
// isUpdatedSystemApp is ignoreable above, only used for shared library adjustment
return parsedPackage.hideAsFinal();
- } catch (PackageManagerException e) {
+ } catch (PackageParserException e) {
throw new RuntimeException(e);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 7b29e2a..538c0ee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -56,6 +56,7 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder
import com.android.internal.R
+import com.android.internal.pm.parsing.PackageParser2
import com.android.internal.pm.parsing.pkg.PackageImpl
import com.android.internal.pm.parsing.pkg.ParsedPackage
import com.android.internal.pm.pkg.parsing.ParsingPackage
@@ -69,7 +70,6 @@
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.pm.dex.DexManager
import com.android.server.pm.dex.DynamicCodeLogger
-import com.android.server.pm.parsing.PackageParser2
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.resolution.ComponentResolver
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
index da929af..7feafef 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -21,6 +21,7 @@
import android.os.Build
import android.os.Process
import android.util.Log
+import com.android.internal.pm.parsing.PackageParserException
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.testutils.whenever
import java.io.File
@@ -120,7 +121,7 @@
argThat { path: File -> path.path.contains("a.data.package") },
anyInt(),
anyBoolean()))
- .thenThrow(PackageManagerException(
+ .thenThrow(PackageParserException(
PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
val pm = createPackageManagerService()
verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index a63d01b..e7da26e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -456,6 +456,14 @@
}
@Test
+ public void avbEnabled_standby_avbDisabled() {
+ enableAbsoluteVolumeBehavior();
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ }
+
+ @Test
public void avbEnabled_cecVolumeDisabled_avbDisabled() {
enableAbsoluteVolumeBehavior();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index ce15c6d..eb9cce0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -67,10 +67,10 @@
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.compat.PlatformCompat;
import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.testutils.TestUtils;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 1bfd43f..25eedf5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -37,6 +37,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.TestPackageParser2;
@@ -161,7 +162,8 @@
}
@Test
- public void testParsePackageWithDmFileValid() throws IOException, PackageManagerException {
+ public void testParsePackageWithDmFileValid() throws IOException, PackageManagerException,
+ PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk");
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
@@ -178,7 +180,7 @@
@Test
public void testParsePackageSplitsWithDmFileValid()
- throws IOException, PackageManagerException {
+ throws IOException, PackageManagerException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
@@ -201,7 +203,7 @@
@Test
public void testParsePackageSplitsNoBaseWithDmFileValid()
- throws IOException, PackageManagerException {
+ throws IOException, PackageManagerException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_feature_a.apk");
@@ -219,7 +221,7 @@
}
@Test
- public void testParsePackageWithDmFileInvalid() throws IOException {
+ public void testParsePackageWithDmFileInvalid() throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
Files.createFile(invalidDmFile.toPath());
@@ -242,7 +244,7 @@
@Test
public void testParsePackageSplitsWithDmFileInvalid()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
@@ -268,7 +270,7 @@
@Test
public void testParsePackageWithDmFileInvalidManifest()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*validManifest=*/false);
@@ -283,7 +285,7 @@
@Test
public void testParsePackageWithDmFileEmptyManifest()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*packageName=*/"doesn't matter",
/*versionCode=*/-12345L, /*emptyManifest=*/true, /*validManifest=*/true);
@@ -299,7 +301,7 @@
@Test
public void testParsePackageWithDmFileBadPackageName()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*packageName=*/"bad package name",
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -315,7 +317,7 @@
@Test
public void testParsePackageWithDmFileBadVersionCode()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
/*versionCode=*/12345L, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -331,7 +333,7 @@
@Test
public void testParsePackageWithDmFileMissingPackageName()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*packageName=*/null,
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -347,7 +349,7 @@
@Test
public void testParsePackageWithDmFileMissingVersionCode()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
/*versionCode=*/null, /*emptyManifest=*/false, /*validManifest=*/true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 9b4ca2a..6a088d9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -36,7 +36,7 @@
@Postsubmit
class AndroidPackageParsingValidationTest {
companion object {
- private val parser2 = PackageParser2.forParsingFileWithDefaults()
+ private val parser2 = PackageParserUtils.forParsingFileWithDefaults()
private val apks = ((PackageManagerService.SYSTEM_PARTITIONS)
.flatMap {
listOfNotNull(it.privAppFolder, it.appFolder, it.overlayFolder)
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
index c44f583..e420e4b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
@@ -18,11 +18,12 @@
import android.content.pm.ApplicationInfo
import android.util.ArraySet
+import com.android.internal.pm.parsing.PackageParser2
import java.io.File
class TestPackageParser2(var cacheDir: File? = null) : PackageParser2(
null /* separateProcesses */, null /* displayMetrics */,
- cacheDir /* cacheDir */, object : PackageParser2.Callback() {
+ cacheDir?.let { PackageCacher(cacheDir) }, object : PackageParser2.Callback() {
override fun isChangeEnabled(changeId: Long, appInfo: ApplicationInfo): Boolean {
return true
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a0e49a6..884ea31 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -13616,7 +13616,31 @@
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
- public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy()
+ throws RemoteException {
+ setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ AssociationRequest.DEVICE_PROFILE_WATCH, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_autoCompanionApp_setsGlobalPolicy()
+ throws RemoteException {
+ setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_otherCompanionApp_doesNotSetGlobalPolicy()
+ throws RemoteException {
+ setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false);
+ }
+
+ private void setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
+ throws RemoteException {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
@@ -13626,14 +13650,19 @@
when(mCompanionMgr.getAssociations(anyString(), anyInt()))
.thenReturn(ImmutableList.of(
new AssociationInfo.Builder(1, mUserId, "package")
- .setDisplayName("My watch")
- .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .setDisplayName("My connected device")
+ .setDeviceProfile(deviceProfile)
.build()));
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
mBinderService.setNotificationPolicy("package", policy, false);
- verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+ if (canSetGlobalPolicy) {
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+ } else {
+ verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(anyString(), anyInt(),
+ eq(policy), anyInt());
+ }
}
@Test
@@ -13703,7 +13732,29 @@
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
- public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ public void setInterruptionFilter_watchCompanionApp_setsGlobalZen() throws RemoteException {
+ setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ AssociationRequest.DEVICE_PROFILE_WATCH, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_autoCompanionApp_setsGlobalZen() throws RemoteException {
+ setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_otherCompanionApp_doesNotSetGlobalZen()
+ throws RemoteException {
+ setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false);
+ }
+
+ private void setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
+ throws RemoteException {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
@@ -13713,14 +13764,19 @@
when(mCompanionMgr.getAssociations(anyString(), anyInt()))
.thenReturn(ImmutableList.of(
new AssociationInfo.Builder(1, mUserId, "package")
- .setDisplayName("My watch")
- .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .setDisplayName("My connected device")
+ .setDeviceProfile(deviceProfile)
.build()));
mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
- verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
- eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
+ if (canSetGlobalPolicy) {
+ verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
+ } else {
+ verify(zenModeHelper).applyGlobalZenModeAsImplicitZenRule(anyString(), anyInt(),
+ eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+ }
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
index d2ef180..ca3787e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
@@ -95,6 +95,26 @@
assertFalse(action.executed);
}
+ @Test
+ public void queueKeyAction_beforeAndAfterCancelQueuedActions_onlyActionsAfterCancelExecuted() {
+ TestAction action1 = new TestAction();
+ TestAction action2 = new TestAction();
+ TestAction action3 = new TestAction();
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action1);
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action2);
+ mKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action3);
+
+ mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1);
+
+ assertFalse(action1.executed);
+ assertFalse(action2.executed);
+ assertTrue(action3.executed);
+ }
+
static class TestAction implements Runnable {
public boolean executed;
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index f7ad2a8..50d37ec 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -19,14 +19,18 @@
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.policy.PhoneWindowManager.DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
+import static com.android.server.policy.PhoneWindowManager.TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY;
import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.ComponentName;
import android.os.RemoteException;
import android.provider.Settings;
@@ -50,6 +54,7 @@
public void stemSingleKey_duringSetup_doNothing() {
overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(false);
@@ -65,6 +70,7 @@
public void stemSingleKey_AfterSetup_openAllApp() {
overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.overrideStartActivity();
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -83,6 +89,7 @@
STEM_PRIMARY_BUTTON_SHORT_PRESS,
SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.overrideStartActivity();
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -104,6 +111,7 @@
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true);
+
setDispatchedKeyHandler(keyEvent -> true);
sendKey(KEYCODE_STEM_PRIMARY);
@@ -131,6 +139,7 @@
STEM_PRIMARY_BUTTON_LONG_PRESS,
LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -144,6 +153,7 @@
STEM_PRIMARY_BUTTON_LONG_PRESS,
LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideSearchManager(null);
mPhoneWindowManager.overrideStatusBarManagerInternal();
@@ -156,7 +166,8 @@
@Test
public void stemDoubleKey_EarlyShortPress_AllAppsThenSwitchToMostRecent()
throws RemoteException {
- overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
@@ -171,14 +182,47 @@
sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
@Test
- public void stemDoubleKey_NoEarlyShortPress_SwitchToMostRecent() throws RemoteException {
+ public void stemTripleKey_EarlyShortPress_AllAppsThenBackToOriginalThenToggleA11y()
+ throws RemoteException {
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
+ mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true);
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ RootTaskInfo allAppsTask = new RootTaskInfo();
+ int referenceId = 777;
+ allAppsTask.taskId = referenceId;
+ doReturn(allAppsTask)
+ .when(mPhoneWindowManager.mActivityManagerService)
+ .getFocusedRootTaskInfo();
+
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ false);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);
+ }
+
+ @Test
+ public void stemMultiKey_NoEarlyPress_NoOpenAllApp() throws RemoteException {
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+ mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
RecentTaskInfo recentTaskInfo = new RecentTaskInfo();
@@ -189,9 +233,16 @@
sendKey(KEYCODE_STEM_PRIMARY);
sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertNotOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertNotOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
@Test
@@ -215,7 +266,7 @@
sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertNotOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
private void overrideBehavior(String key, int expectedBehavior) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 0678210..7c2f7ee 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -73,6 +73,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
@@ -176,8 +177,9 @@
private Handler mHandler;
private boolean mIsTalkBackEnabled;
+ private boolean mIsTalkBackShortcutGestureEnabled;
- class TestTalkbackShortcutController extends TalkbackShortcutController {
+ private class TestTalkbackShortcutController extends TalkbackShortcutController {
TestTalkbackShortcutController(Context context) {
super(context);
}
@@ -190,13 +192,18 @@
@Override
boolean isTalkBackShortcutGestureEnabled() {
- return true;
+ return mIsTalkBackShortcutGestureEnabled;
}
}
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
- super(context, funcs, mTestLooper.getLooper());
+ super(context, funcs);
+ }
+
+ @Override
+ Looper getLooper() {
+ return mTestLooper.getLooper();
}
AccessibilityShortcutController getAccessibilityShortcutController(
@@ -410,6 +417,10 @@
mPhoneWindowManager.mShouldEarlyShortPressOnStemPrimary = shouldEarlyShortPress;
}
+ void overrideTalkbackShortcutGestureEnabled(boolean enabled) {
+ mIsTalkBackShortcutGestureEnabled = enabled;
+ }
+
// Override assist perform function.
void overrideLongPressOnPower(int behavior) {
mPhoneWindowManager.mLongPressOnPowerBehavior = behavior;
@@ -714,7 +725,7 @@
}
void assertOpenAllAppView() {
- mTestLooper.dispatchAll();
+ moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
.startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -728,7 +739,7 @@
}
void assertActivityTargetLaunched(ComponentName targetActivity) {
- mTestLooper.dispatchAll();
+ moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
.startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -743,10 +754,15 @@
expectedModifierState, deviceBus), description(errorMsg));
}
- void assertSwitchToRecent(int persistentId) throws RemoteException {
+ void assertSwitchToTask(int persistentId) throws RemoteException {
mTestLooper.dispatchAll();
verify(mActivityManagerService,
timeout(TEST_SINGLE_KEY_DELAY_MILLIS)).startActivityFromRecents(eq(persistentId),
isNull());
}
+
+ void assertTalkBack(boolean expectEnabled) {
+ mTestLooper.dispatchAll();
+ Assert.assertEquals(expectEnabled, mIsTalkBackEnabled);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 85c6f9e..718c598 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -202,8 +202,7 @@
}
private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
- return new TestStartingWindowOrganizer(mAtm,
- mSystemServicesTestRule.getPowerManagerWrapper());
+ return new TestStartingWindowOrganizer(mAtm);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 51f0404..8cd9ff3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -134,7 +134,6 @@
private StaticMockitoSession mMockitoSession;
private ActivityTaskManagerService mAtmService;
private WindowManagerService mWmService;
- private WindowState.PowerManagerWrapper mPowerManagerWrapper;
private InputManagerService mImService;
private InputChannel mInputChannel;
private Runnable mOnBeforeServicesCreated;
@@ -360,7 +359,6 @@
}
private void setUpWindowManagerService() {
- mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy();
TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider =
new TestDisplayWindowSettingsProvider();
@@ -485,10 +483,6 @@
return mAtmService;
}
- WindowState.PowerManagerWrapper getPowerManagerWrapper() {
- return mPowerManagerWrapper;
- }
-
/** Creates a no-op wakelock object. */
PowerManager.WakeLock createStubbedWakeLock(boolean needVerification) {
if (needVerification) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 2007f68..75e252f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -52,7 +52,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -368,28 +367,26 @@
firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- final WindowState.PowerManagerWrapper powerManagerWrapper =
- mSystemServicesTestRule.getPowerManagerWrapper();
- reset(powerManagerWrapper);
+ final var powerManager = mWm.mPowerManager;
+ clearInvocations(powerManager);
firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
- reset(powerManagerWrapper);
+ clearInvocations(powerManager);
secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
}
private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) {
- final WindowState.PowerManagerWrapper powerManagerWrapper =
- mSystemServicesTestRule.getPowerManagerWrapper();
- reset(powerManagerWrapper);
+ final var powerManager = mWm.mPowerManager;
+ clearInvocations(powerManager);
appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
if (expectedWakeupCalled) {
- verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
} else {
- verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
}
// If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
// because the state will be consumed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 616a23e..a5f6190f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -637,14 +637,12 @@
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) {
return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId),
- ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow,
- mSystemServicesTestRule.getPowerManagerWrapper());
+ ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token,
String name, int ownerId, int userId, boolean ownerCanAddInternalSystemWindow,
- WindowManagerService service, Session session, IWindow iWindow,
- WindowState.PowerManagerWrapper powerManagerWrapper) {
+ WindowManagerService service, Session session, IWindow iWindow) {
SystemServicesTestRule.checkHoldsLock(service.mGlobalLock);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
@@ -652,9 +650,7 @@
attrs.packageName = "test";
final WindowState w = new WindowState(service, session, iWindow, token, parent,
- OP_NONE, attrs, VISIBLE, ownerId, userId,
- ownerCanAddInternalSystemWindow,
- powerManagerWrapper);
+ OP_NONE, attrs, VISIBLE, ownerId, userId, ownerCanAddInternalSystemWindow);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
@@ -1738,17 +1734,14 @@
static class TestStartingWindowOrganizer extends WindowOrganizerTests.StubOrganizer {
private final ActivityTaskManagerService mAtm;
private final WindowManagerService mWMService;
- private final WindowState.PowerManagerWrapper mPowerManagerWrapper;
private Runnable mRunnableWhenAddingSplashScreen;
private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>();
private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>();
- TestStartingWindowOrganizer(ActivityTaskManagerService service,
- WindowState.PowerManagerWrapper powerManagerWrapper) {
+ TestStartingWindowOrganizer(ActivityTaskManagerService service) {
mAtm = service;
mWMService = mAtm.mWindowManager;
- mPowerManagerWrapper = powerManagerWrapper;
mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
}
@@ -1767,8 +1760,7 @@
final WindowState window = WindowTestsBase.createWindow(null,
TYPE_APPLICATION_STARTING, activity,
"Starting window", 0 /* ownerId */, 0 /* userId*/,
- false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow,
- mPowerManagerWrapper);
+ false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow);
activity.mStartingWindow = window;
mAppWindowMap.put(info.appToken, window);
mTaskAppMap.put(info.taskInfo.taskId, info.appToken);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 4c978ad..2445f51 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -28,6 +28,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.usage.ExternalStorageStats;
+import android.app.usage.Flags;
import android.app.usage.IStorageStatsManager;
import android.app.usage.StorageStats;
import android.app.usage.UsageStatsManagerInternal;
@@ -434,6 +435,7 @@
final long[] ceDataInodes = new long[packageNames.length];
String[] codePaths = new String[0];
+ final PackageStats stats = new PackageStats(TAG);
for (int i = 0; i < packageNames.length; i++) {
try {
final ApplicationInfo appInfo = mPackage.getApplicationInfoAsUser(packageNames[i],
@@ -443,7 +445,11 @@
} else {
if (appInfo.getCodePath() != null) {
codePaths = ArrayUtils.appendElement(String.class, codePaths,
- appInfo.getCodePath());
+ appInfo.getCodePath());
+ }
+ if (Flags.getAppBytesByDataTypeApi()) {
+ computeAppStatsByDataTypes(
+ stats, appInfo.sourceDir);
}
}
} catch (NameNotFoundException e) {
@@ -451,7 +457,6 @@
}
}
- final PackageStats stats = new PackageStats(TAG);
try {
mInstaller.getAppSize(volumeUuid, packageNames, userId, getDefaultFlags(),
appId, ceDataInodes, codePaths, stats);
@@ -587,6 +592,9 @@
res.codeBytes = stats.codeSize + stats.externalCodeSize;
res.dataBytes = stats.dataSize + stats.externalDataSize;
res.cacheBytes = stats.cacheSize + stats.externalCacheSize;
+ res.apkBytes = stats.apkSize;
+ res.libBytes = stats.libSize;
+ res.dmBytes = stats.dmSize;
res.externalCacheBytes = stats.externalCacheSize;
return res;
}
@@ -894,4 +902,61 @@
mStorageStatsAugmenters.add(Pair.create(tag, storageStatsAugmenter));
}
}
+
+ private long getDirBytes(File dir) {
+ if (!dir.isDirectory()) {
+ return 0;
+ }
+
+ long size = 0;
+ try {
+ for (File file : dir.listFiles()) {
+ if (file.isFile()) {
+ size += file.length();
+ continue;
+ }
+ if (file.isDirectory()) {
+ size += getDirBytes(file);
+ }
+ }
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed to list directory " + dir.getName());
+ }
+
+ return size;
+ }
+
+ private long getFileBytesInDir(File dir, String suffix) {
+ if (!dir.isDirectory()) {
+ return 0;
+ }
+
+ long size = 0;
+ try {
+ for (File file : dir.listFiles()) {
+ if (file.isFile() && file.getName().endsWith(suffix)) {
+ size += file.length();
+ }
+ }
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed to list directory " + dir.getName());
+ }
+
+ return size;
+ }
+
+ private void computeAppStatsByDataTypes(
+ PackageStats stats, String sourceDirName) {
+
+ // Get apk, lib, dm file sizes.
+ File srcDir = new File(sourceDirName);
+ if (srcDir.isFile()) {
+ sourceDirName = srcDir.getParent();
+ srcDir = new File(sourceDirName);
+ }
+
+ stats.apkSize += getFileBytesInDir(srcDir, ".apk");
+ stats.dmSize += getFileBytesInDir(srcDir, ".dm");
+ stats.libSize += getDirBytes(new File(sourceDirName + "/lib/"));
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ccd4ce0..08f719e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1121,13 +1121,8 @@
switch (event.mEventType) {
case Event.ACTIVITY_RESUMED:
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
- uid,
- event.mPackage,
- "",
- FrameworkStatsLog
- .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND);
+ logAppUsageEventReportedAtomLocked(Event.ACTIVITY_RESUMED, uid, event.mPackage);
+
// check if this activity has already been resumed
if (mVisibleActivities.get(event.mInstanceId) != null) break;
final String usageSourcePackage = getUsageSourcePackage(event);
@@ -1172,13 +1167,8 @@
usageSourcePackage2);
mVisibleActivities.put(event.mInstanceId, pausedData);
} else {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
- uid,
- event.mPackage,
- "",
- FrameworkStatsLog
- .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+ logAppUsageEventReportedAtomLocked(Event.ACTIVITY_PAUSED, uid,
+ event.mPackage);
}
pausedData.lastEvent = Event.ACTIVITY_PAUSED;
@@ -1203,13 +1193,8 @@
}
if (prevData.lastEvent != Event.ACTIVITY_PAUSED) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
- uid,
- event.mPackage,
- "",
- FrameworkStatsLog
- .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+ logAppUsageEventReportedAtomLocked(Event.ACTIVITY_PAUSED, uid,
+ event.mPackage);
}
ArraySet<String> tokens;
@@ -1244,11 +1229,19 @@
}
break;
case Event.USER_INTERACTION:
- // Fall through
+ logAppUsageEventReportedAtomLocked(Event.USER_INTERACTION, uid, event.mPackage);
+ // Fall through.
case Event.APP_COMPONENT_USED:
convertToSystemTimeLocked(event);
mLastTimeComponentUsedGlobal.put(event.mPackage, event.mTimeStamp);
break;
+ case Event.SHORTCUT_INVOCATION:
+ case Event.CHOOSER_ACTION:
+ case Event.STANDBY_BUCKET_CHANGED:
+ case Event.FOREGROUND_SERVICE_START:
+ case Event.FOREGROUND_SERVICE_STOP:
+ logAppUsageEventReportedAtomLocked(event.mEventType, uid, event.mPackage);
+ break;
}
final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
@@ -1261,6 +1254,45 @@
mIoHandler.obtainMessage(MSG_NOTIFY_USAGE_EVENT_LISTENER, userId, 0, event).sendToTarget();
}
+ @GuardedBy("mLock")
+ private void logAppUsageEventReportedAtomLocked(int eventType, int uid, String packageName) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED, uid, packageName,
+ "", getAppUsageEventOccurredAtomEventType(eventType));
+ }
+
+ /** Make sure align with the EventType defined in the AppUsageEventOccurred atom. */
+ private int getAppUsageEventOccurredAtomEventType(int eventType) {
+ switch (eventType) {
+ case Event.ACTIVITY_RESUMED:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND;
+ case Event.ACTIVITY_PAUSED:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND;
+ case Event.USER_INTERACTION:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__USER_INTERACTION;
+ case Event.SHORTCUT_INVOCATION:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__SHORTCUT_INVOCATION;
+ case Event.CHOOSER_ACTION:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__CHOOSER_ACTION;
+ case Event.STANDBY_BUCKET_CHANGED:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__STANDBY_BUCKET_CHANGED;
+ case Event.FOREGROUND_SERVICE_START:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__FOREGROUND_SERVICE_START;
+ case Event.FOREGROUND_SERVICE_STOP:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__FOREGROUND_SERVICE_STOP;
+ default:
+ Slog.w(TAG, "Unsupported usage event logging: " + eventType);
+ return -1;
+ }
+ }
+
private String getUsageSourcePackage(Event event) {
switch(mUsageSource) {
case USAGE_SOURCE_CURRENT_ACTIVITY:
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
deleted file mode 100644
index 1e43c71..0000000
--- a/telecomm/java/android/telecom/AuthenticatorService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telecom;
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
-/**
- * A generic stub account authenticator service often used for sync adapters that do not directly
- * involve accounts.
- *
- * @hide
- */
-public class AuthenticatorService extends Service {
- private static Authenticator mAuthenticator;
-
- @Override
- public void onCreate() {
- mAuthenticator = new Authenticator(this);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mAuthenticator.getIBinder();
- }
-
- /**
- * Stub account authenticator. All methods either return null or throw an exception.
- */
- public class Authenticator extends AbstractAccountAuthenticator {
- public Authenticator(Context context) {
- super(context);
- }
-
- @Override
- public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
- String s) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse,
- String s, String s2, String[] strings, Bundle bundle)
- throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, Bundle bundle)
- throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, String s, Bundle bundle)
- throws NetworkErrorException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getAuthTokenLabel(String s) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, String s, Bundle bundle)
- throws NetworkErrorException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, String[] strings)
- throws NetworkErrorException {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ee9bf898..a2105b0 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3340,15 +3340,6 @@
public void onDisconnect() {}
/**
- * Notifies this Connection of a request to disconnect a participant of the conference managed
- * by the connection.
- *
- * @param endpoint the {@link Uri} of the participant to disconnect.
- * @hide
- */
- public void onDisconnectConferenceParticipant(Uri endpoint) {}
-
- /**
* Notifies this Connection of a request to separate from its parent conference.
*/
public void onSeparate() {}
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index db38f88..575ec27 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -187,14 +187,15 @@
}
for (ResolveInfo r : packages) {
- if (r.activityInfo == null
- || pm.checkPermission(
+ if (r.activityInfo == null) {
+ Rlog.w(TAG, "Found package without activity");
+ continue;
+ } else if (pm.checkPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
r.activityInfo.packageName)
- != PackageManager.PERMISSION_GRANTED) {
- Rlog.w(TAG,
- "Found package without proper permissions or no activity"
- + r.activityInfo.packageName);
+ != PackageManager.PERMISSION_GRANTED) {
+ Rlog.w(TAG, "Found package without proper permissions"
+ + r.activityInfo.packageName);
continue;
}
Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 101d285..c7b84a3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3721,19 +3721,19 @@
* This configuration allows the system UI to display different 5G icons for different 5G
* scenarios.
*
- * There are five 5G scenarios:
- * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
- * millimeter wave.
- * 2. connected: device currently connected to 5G cell as the secondary cell but not using
- * millimeter wave.
- * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability(not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in IDLE state.
- * 4. not_restricted_rrc_con: device camped on a network that has 5G capability(not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in CONNECTED state.
- * 5. restricted: device camped on a network that has 5G capability(not necessary to connect a
- * 5G cell as a secondary cell) but the use of 5G is restricted.
+ * There are six 5G scenarios for icon configuration:
+ * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+ * and considered NR advanced.
+ * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+ * considered NR advanced.
+ * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+ * and RRC currently in IDLE state.
+ * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in IDLE state.
+ * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in CONNECTED state.
+ * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+ * restricted.
*
* The configured string contains multiple key-value pairs separated by comma. For each pair,
* the key and value are separated by a colon. The key corresponds to a 5G status above and
@@ -3754,21 +3754,21 @@
* This configuration allows the system UI to determine how long to continue to display 5G icons
* when the device switches between different 5G scenarios.
*
- * There are seven 5G scenarios:
- * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
- * millimeter wave.
- * 2. connected: device currently connected to 5G cell as the secondary cell but not using
- * millimeter wave.
- * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in IDLE state.
- * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in CONNECTED state.
- * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a
- * 5G cell as a secondary cell) but the use of 5G is restricted.
- * 6. legacy: device is not camped on a network that has 5G capability
- * 7. any: any of the above scenarios
+ * There are eight 5G scenarios:
+ * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+ * and considered NR advanced.
+ * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+ * considered NR advanced.
+ * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+ * and RRC currently in IDLE state.
+ * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in IDLE state.
+ * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in CONNECTED state.
+ * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+ * restricted.
+ * 7. legacy: device is not camped on a network that has 5G capability
+ * 8. any: any of the above scenarios
*
* The configured string contains various timer rules separated by a semicolon.
* Each rule will have three items: prior 5G scenario, current 5G scenario, and grace period
@@ -3776,8 +3776,8 @@
* 5G scenario, the system UI will continue to show the icon for the prior 5G scenario (defined
* in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace
* period. If the prior 5G scenario is reestablished, the timer will reset and start again if
- * the UE changes 5G scenarios again. Defined states (5G scenarios #1-5) take precedence over
- * 'any' (5G scenario #6), and unspecified transitions have a default grace period of 0.
+ * the UE changes 5G scenarios again. Defined states (5G scenarios #1-7) take precedence over
+ * 'any' (5G scenario #8), and unspecified transitions have a default grace period of 0.
* The order of rules in the configuration determines the priority (the first applicable timer
* rule will be used).
*
@@ -3800,21 +3800,21 @@
* This configuration extends {@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING} to allow the
* system UI to continue displaying 5G icons after the initial timer expires.
*
- * There are seven 5G scenarios:
- * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
- * millimeter wave.
- * 2. connected: device currently connected to 5G cell as the secondary cell but not using
- * millimeter wave.
- * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in IDLE state.
- * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in CONNECTED state.
- * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a
- * 5G cell as a secondary cell) but the use of 5G is restricted.
- * 6. legacy: device is not camped on a network that has 5G capability
- * 7. any: any of the above scenarios
+ * There are eight 5G scenarios:
+ * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+ * and considered NR advanced.
+ * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+ * considered NR advanced.
+ * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+ * and RRC currently in IDLE state.
+ * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in IDLE state.
+ * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in CONNECTED state.
+ * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+ * restricted.
+ * 7. legacy: device is not camped on a network that has 5G capability
+ * 8. any: any of the above scenarios
*
* The configured string contains various timer rules separated by a semicolon.
* Each rule will have three items: primary 5G scenario, secondary 5G scenario, and
@@ -3824,7 +3824,7 @@
* period. If the primary 5G scenario is reestablished, the timers will reset and the system UI
* will continue to display the icon for the primary 5G scenario without interruption. If the
* secondary 5G scenario is lost, the timer will reset and the icon will reflect the true state.
- * Defined states (5G scenarios #1-5) take precedence over 'any' (5G scenario #6), and
+ * Defined states (5G scenarios #1-7) take precedence over 'any' (5G scenario #8), and
* unspecified transitions have a default grace period of 0. The order of rules in the
* configuration determines the priority (the first applicable timer rule will be used).
*
@@ -8891,18 +8891,18 @@
KEY_PREFIX + "epdg_static_address_roaming_string";
/**
- * Controls if the multiple SA proposals allowed for IKE session to include
- * all the 3GPP TS 33.210 and RFC 8221 supported cipher suites in multiple
- * IKE SA proposals as per RFC 7296.
+ * Enables the use of multiple IKE SA proposals, encompassing both carrier-preferred
+ * ciphers and all supported ciphers from 3GPP TS 33.210 and RFC 8221,
+ * as defined in RFC 7296.
*/
@FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS)
public static final String KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL =
KEY_PREFIX + "supports_ike_session_multiple_sa_proposals_bool";
/**
- * Controls if the multiple SA proposals allowed for Child session to include
- * all the 3GPP TS 33.210 and RFC 8221 supported cipher suites in multiple
- * Child SA proposals as per RFC 7296.
+ * Enables the use of multiple Child SA proposals, encompassing both carrier-preferred
+ * ciphers and all supported ciphers from 3GPP TS 33.210 and RFC 8221,
+ * as defined in RFC 7296.
*/
@FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS)
public static final String KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL =
@@ -10050,6 +10050,49 @@
public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE =
"auto_data_switch_rat_signal_score_string_bundle";
+ // TODO(b/316183370): replace @code with @link in javadoc after feature is released
+ /**
+ * An array of cellular services supported by a subscription.
+ *
+ * <p>Permissible values include:
+ * <ul>
+ * <li>{@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} for voice services</li>
+ * <li>{@code SubscriptionManager#SERVICE_CAPABILITY_SMS} for SMS services</li>
+ * <li>{@code SubscriptionManager#SERVICE_CAPABILITY_DATA} for data services</li>
+ * </ul>
+ *
+ * <p>Carrier-specific factors may influence how these services are supported. Therefore,
+ * modifying this carrier configuration might not always enable the specified services. These
+ * capability bitmasks should be considered as indicators of a carrier's preferred services
+ * to enhance user experience, rather than as absolute platform guarantees.
+ *
+ * <p>Device-level service capabilities, defined by
+ * {@code TelephonyManager#isDeviceVoiceCapable} and
+ * {@code TelephonyManager#isDeviceSmsCapable}, take precedence over these subscription-level
+ * settings. For instance, a device where {@code TelephonyManager#isDeviceVoiceCapable} returns
+ * false may not be able to make voice calls, even if subscribed to a service marked as
+ * voice-capable.
+ *
+ * <p>To determine a subscription's cellular service capabilities, use
+ * {@code SubscriptionInfo#getServiceCapabilities()}. To track changes in services, register
+ * a {@link SubscriptionManager.OnSubscriptionsChangedListener} and invoke the
+ * same method in its callback.
+ *
+ * <p>Emergency service availability may not depend on the cellular service capabilities.
+ * For example, emergency calls might be possible on a subscription even if it lacks
+ * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE}.
+ *
+ * <p>If unset, the default value is “[1, 2, 3]” (supports all cellular services).
+ *
+ * @see TelephonyManager#isDeviceVoiceCapable
+ * @see TelephonyManager#isDeviceSmsCapable
+ * @see SubscriptionInfo#getServiceCapabilities()
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY =
+ "cellular_service_capabilities_int_array";
+
/** The default value for every variable. */
private static final PersistableBundle sDefaults;
@@ -10565,7 +10608,7 @@
sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false);
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
- "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
+ "connected_mmwave:5G,connected:5G,connected_rrc_idle:5G,not_restricted_rrc_idle:5G,"
+ "not_restricted_rrc_con:5G");
sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
@@ -10837,6 +10880,7 @@
new boolean[] {false, false, true, false, false});
sDefaults.putStringArray(KEY_CARRIER_SERVICE_NAME_STRING_ARRAY, new String[0]);
sDefaults.putStringArray(KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY, new String[0]);
+ sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
}
/**
diff --git a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
new file mode 100644
index 0000000..61d7ead
--- /dev/null
+++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A single occurrence capturing a notable change to previously reported
+ * cryptography algorithms for a given network and network event.
+ *
+ * @hide
+ */
+public final class SecurityAlgorithmUpdate implements Parcelable {
+ private static final String TAG = "SecurityAlgorithmUpdate";
+
+ private @ConnectionEvent int mConnectionEvent;
+ private @SecurityAlgorithm int mEncryption;
+ private @SecurityAlgorithm int mIntegrity;
+ private boolean mIsUnprotectedEmergency;
+
+ public SecurityAlgorithmUpdate(@ConnectionEvent int connectionEvent,
+ @SecurityAlgorithm int encryption, @SecurityAlgorithm int integrity,
+ boolean isUnprotectedEmergency) {
+ mConnectionEvent = connectionEvent;
+ mEncryption = encryption;
+ mIntegrity = integrity;
+ mIsUnprotectedEmergency = isUnprotectedEmergency;
+ }
+
+ private SecurityAlgorithmUpdate(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public @ConnectionEvent int getConnectionEvent() {
+ return mConnectionEvent;
+ }
+
+ public @SecurityAlgorithm int getEncryption() {
+ return mEncryption;
+ }
+
+ public @SecurityAlgorithm int getIntegrity() {
+ return mIntegrity;
+ }
+
+ public boolean isUnprotectedEmergency() {
+ return mIsUnprotectedEmergency;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mConnectionEvent);
+ out.writeInt(mEncryption);
+ out.writeInt(mIntegrity);
+ out.writeBoolean(mIsUnprotectedEmergency);
+ }
+
+ private void readFromParcel(@NonNull Parcel in) {
+ mConnectionEvent = in.readInt();
+ mEncryption = in.readInt();
+ mIntegrity = in.readInt();
+ mIsUnprotectedEmergency = in.readBoolean();
+ }
+
+ public static final Parcelable.Creator<SecurityAlgorithmUpdate> CREATOR =
+ new Parcelable.Creator<SecurityAlgorithmUpdate>() {
+ public SecurityAlgorithmUpdate createFromParcel(Parcel in) {
+ return new SecurityAlgorithmUpdate(in);
+ }
+
+ public SecurityAlgorithmUpdate[] newArray(int size) {
+ return new SecurityAlgorithmUpdate[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return TAG + ":{ mConnectionEvent = " + mConnectionEvent + " mEncryption = " + mEncryption
+ + " mIntegrity = " + mIntegrity + " mIsUnprotectedEmergency = "
+ + mIsUnprotectedEmergency;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SecurityAlgorithmUpdate)) return false;
+ SecurityAlgorithmUpdate that = (SecurityAlgorithmUpdate) o;
+ return mConnectionEvent == that.mConnectionEvent
+ && mEncryption == that.mEncryption
+ && mIntegrity == that.mIntegrity
+ && mIsUnprotectedEmergency == that.mIsUnprotectedEmergency;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConnectionEvent, mEncryption, mIntegrity, mIsUnprotectedEmergency);
+ }
+
+ public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0;
+ public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1;
+ public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2;
+ public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3;
+ public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4;
+ public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5;
+ public static final int CONNECTION_EVENT_VOLTE_SIP = 6;
+ public static final int CONNECTION_EVENT_VOLTE_RTP = 7;
+ public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 8;
+ public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 9;
+ public static final int CONNECTION_EVENT_VONR_SIP = 10;
+ public static final int CONNECTION_EVENT_VONR_RTP = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {CONNECTION_EVENT_CS_SIGNALLING_GSM,
+ CONNECTION_EVENT_PS_SIGNALLING_GPRS, CONNECTION_EVENT_CS_SIGNALLING_3G,
+ CONNECTION_EVENT_PS_SIGNALLING_3G, CONNECTION_EVENT_NAS_SIGNALLING_LTE,
+ CONNECTION_EVENT_AS_SIGNALLING_LTE, CONNECTION_EVENT_VOLTE_SIP,
+ CONNECTION_EVENT_VOLTE_RTP, CONNECTION_EVENT_NAS_SIGNALLING_5G,
+ CONNECTION_EVENT_AS_SIGNALLING_5G, CONNECTION_EVENT_VONR_SIP,
+ CONNECTION_EVENT_VONR_RTP})
+ public @interface ConnectionEvent {
+ }
+
+ public static final int SECURITY_ALGORITHM_A50 = 0;
+ public static final int SECURITY_ALGORITHM_A51 = 1;
+ public static final int SECURITY_ALGORITHM_A52 = 2;
+ public static final int SECURITY_ALGORITHM_A53 = 3;
+ public static final int SECURITY_ALGORITHM_A54 = 4;
+ public static final int SECURITY_ALGORITHM_GEA0 = 14;
+ public static final int SECURITY_ALGORITHM_GEA1 = 15;
+ public static final int SECURITY_ALGORITHM_GEA2 = 16;
+ public static final int SECURITY_ALGORITHM_GEA3 = 17;
+ public static final int SECURITY_ALGORITHM_GEA4 = 18;
+ public static final int SECURITY_ALGORITHM_GEA5 = 19;
+ public static final int SECURITY_ALGORITHM_UEA0 = 29;
+ public static final int SECURITY_ALGORITHM_UEA1 = 30;
+ public static final int SECURITY_ALGORITHM_UEA2 = 31;
+ public static final int SECURITY_ALGORITHM_EEA0 = 41;
+ public static final int SECURITY_ALGORITHM_EEA1 = 42;
+ public static final int SECURITY_ALGORITHM_EEA2 = 43;
+ public static final int SECURITY_ALGORITHM_EEA3 = 44;
+ public static final int SECURITY_ALGORITHM_NEA0 = 55;
+ public static final int SECURITY_ALGORITHM_NEA1 = 56;
+ public static final int SECURITY_ALGORITHM_NEA2 = 57;
+ public static final int SECURITY_ALGORITHM_NEA3 = 58;
+ public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
+ public static final int SECURITY_ALGORITHM_AES_GCM = 69;
+ public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
+ public static final int SECURITY_ALGORITHM_AES_CBC = 71;
+ public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
+ public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
+ public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
+ public static final int SECURITY_ALGORITHM_HMAC_SHA1_96_NULL = 75;
+ public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 76;
+ public static final int SECURITY_ALGORITHM_HMAC_MD5_96_NULL = 77;
+ public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
+ public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
+ public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
+ public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99;
+ public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100;
+ public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101;
+ public static final int SECURITY_ALGORITHM_UNKNOWN = 113;
+ public static final int SECURITY_ALGORITHM_OTHER = 114;
+ public static final int SECURITY_ALGORITHM_ORYX = 124;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {SECURITY_ALGORITHM_A50, SECURITY_ALGORITHM_A51,
+ SECURITY_ALGORITHM_A52, SECURITY_ALGORITHM_A53,
+ SECURITY_ALGORITHM_A54, SECURITY_ALGORITHM_GEA0, SECURITY_ALGORITHM_GEA1,
+ SECURITY_ALGORITHM_GEA2, SECURITY_ALGORITHM_GEA3, SECURITY_ALGORITHM_GEA4,
+ SECURITY_ALGORITHM_GEA5, SECURITY_ALGORITHM_UEA0, SECURITY_ALGORITHM_UEA1,
+ SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
+ SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
+ SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
+ SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
+ SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
+ SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
+ SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_SHA1_96_NULL,
+ SECURITY_ALGORITHM_HMAC_MD5_96, SECURITY_ALGORITHM_HMAC_MD5_96_NULL,
+ SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
+ SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
+ SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
+ SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
+ public @interface SecurityAlgorithm {
+ }
+
+}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 6ebf3be..a188581 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -54,6 +54,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* A Parcelable class for Subscription Information.
@@ -262,6 +263,11 @@
private final boolean mIsOnlyNonTerrestrialNetwork;
/**
+ * The service capabilities (in the form of bitmask combination) the subscription supports.
+ */
+ private final int mServiceCapabilities;
+
+ /**
* @hide
*
* @deprecated Use {@link SubscriptionInfo.Builder}.
@@ -386,6 +392,7 @@
this.mPortIndex = portIndex;
this.mUsageSetting = usageSetting;
this.mIsOnlyNonTerrestrialNetwork = false;
+ this.mServiceCapabilities = 0;
}
/**
@@ -425,6 +432,7 @@
this.mPortIndex = builder.mPortIndex;
this.mUsageSetting = builder.mUsageSetting;
this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
+ this.mServiceCapabilities = builder.mServiceCapabilities;
}
/**
@@ -882,6 +890,44 @@
return mIsOnlyNonTerrestrialNetwork;
}
+ // TODO(b/316183370): replace @code with @link in javadoc after feature is released
+ /**
+ * Retrieves the service capabilities for the current subscription.
+ *
+ * <p>These capabilities are hint to system components and applications, allowing them to
+ * enhance user experience. For instance, a Dialer application can inform the user that the
+ * current subscription is incapable of making voice calls if the voice service is not
+ * available.
+ *
+ * <p>Correct usage of these service capabilities must also consider the device's overall
+ * service capabilities. For example, even if the subscription supports voice calls, a voice
+ * call might not be feasible on a device that only supports data services. To determine the
+ * device's capabilities for voice and SMS services, refer to
+ * {@code TelephonyManager#isDeviceVoiceCapable()} and
+ * {@code TelephonyManager#isDeviceSmsCapable()}.
+ *
+ * <p>Emergency service availability may not directly correlate with the subscription or
+ * device's general service capabilities. In some cases, emergency calls might be possible
+ * even if the subscription or device does not typically support voice services.
+ *
+ * @return A set of integer representing the subscription's service capabilities,
+ * defined by {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE},
+ * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS}
+ * and {@code SubscriptionManager#SERVICE_CAPABILITY_DATA}.
+ *
+ * @see TelephonyManager#isDeviceVoiceCapable()
+ * @see TelephonyManager#isDeviceSmsCapable()
+ * @see CarrierConfigManager#KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY
+ * @see SubscriptionManager#SERVICE_CAPABILITY_VOICE
+ * @see SubscriptionManager#SERVICE_CAPABILITY_SMS
+ * @see SubscriptionManager#SERVICE_CAPABILITY_DATA
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
+ return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
+ }
+
@NonNull
public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
new Parcelable.Creator<SubscriptionInfo>() {
@@ -919,6 +965,8 @@
.setUiccApplicationsEnabled(source.readBoolean())
.setUsageSetting(source.readInt())
.setOnlyNonTerrestrialNetwork(source.readBoolean())
+ .setServiceCapabilities(
+ SubscriptionManager.getServiceCapabilitiesSet(source.readInt()))
.build();
}
@@ -961,6 +1009,7 @@
dest.writeBoolean(mAreUiccApplicationsEnabled);
dest.writeInt(mUsageSetting);
dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
+ dest.writeInt(mServiceCapabilities);
}
@Override
@@ -1024,6 +1073,8 @@
+ " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+ " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
+ + " serviceCapabilities=" + SubscriptionManager.getServiceCapabilitiesSet(
+ mServiceCapabilities).toString()
+ "]";
}
@@ -1049,7 +1100,8 @@
that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
&& mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
- && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork;
+ && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
+ && mServiceCapabilities == that.mServiceCapabilities;
}
@Override
@@ -1058,7 +1110,7 @@
mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
- mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork);
+ mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork, mServiceCapabilities);
result = 31 * result + Arrays.hashCode(mEhplmns);
result = 31 * result + Arrays.hashCode(mHplmns);
result = 31 * result + Arrays.hashCode(mNativeAccessRules);
@@ -1263,6 +1315,11 @@
private boolean mIsOnlyNonTerrestrialNetwork = false;
/**
+ * Service capabilities bitmasks the subscription supports.
+ */
+ private int mServiceCapabilities = 0;
+
+ /**
* Default constructor.
*/
public Builder() {
@@ -1305,6 +1362,7 @@
mPortIndex = info.mPortIndex;
mUsageSetting = info.mUsageSetting;
mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
+ mServiceCapabilities = info.mServiceCapabilities;
}
/**
@@ -1703,6 +1761,32 @@
}
/**
+ * Set the service capabilities that the subscription supports.
+ *
+ * @param capabilities Bitmask combination of SubscriptionManager
+ * .SERVICE_CAPABILITY_XXX.
+ * @return The builder.
+ *
+ * @throws IllegalArgumentException when any capability is not supported.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public Builder setServiceCapabilities(
+ @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
+ int combinedCapabilities = 0;
+ for (int capability : capabilities) {
+ if (capability < SubscriptionManager.SERVICE_CAPABILITY_VOICE
+ || capability > SubscriptionManager.SERVICE_CAPABILITY_MAX) {
+ throw new IllegalArgumentException(
+ "Invalid service capability value: " + capability);
+ }
+ combinedCapabilities |= SubscriptionManager.serviceCapabilityToBitmask(capability);
+ }
+ mServiceCapabilities = combinedCapabilities;
+ return this;
+ }
+
+ /**
* Build the {@link SubscriptionInfo}.
*
* @return The {@link SubscriptionInfo} instance.
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 326b6f5..6c8663a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -88,10 +88,12 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -1138,6 +1140,14 @@
*/
public static final String IS_NTN = SimInfo.COLUMN_IS_NTN;
+ /**
+ * TelephonyProvider column name to identify service capabilities.
+ * Disabled by default.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String SERVICE_CAPABILITIES = SimInfo.COLUMN_SERVICE_CAPABILITIES;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"USAGE_SETTING_"},
@@ -1347,6 +1357,86 @@
})
public @interface PhoneNumberSource {}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SERVICE_CAPABILITY"},
+ value = {
+ SERVICE_CAPABILITY_VOICE,
+ SERVICE_CAPABILITY_SMS,
+ SERVICE_CAPABILITY_DATA,
+ })
+ public @interface ServiceCapability {
+ }
+
+ /**
+ * Represents a value indicating the voice calling capabilities of a subscription.
+ *
+ * <p>This value identifies whether the subscription supports various voice calling services.
+ * These services can include circuit-switched (CS) calling, packet-switched (PS) IMS (IP
+ * Multimedia Subsystem) calling, and over-the-top (OTT) calling options.
+ *
+ * <p>Note: The availability of emergency calling services is not solely dependent on this
+ * voice capability. Emergency services may be accessible even if the subscription lacks
+ * standard voice capabilities. However, the device's ability to support emergency calls
+ * can be influenced by its inherent voice capabilities, as determined by
+ * {@link TelephonyManager#isDeviceVoiceCapable()}.
+ *
+ * @see TelephonyManager#isDeviceVoiceCapable()
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final int SERVICE_CAPABILITY_VOICE = 1;
+
+ /**
+ * Represents a value indicating the SMS capabilities of a subscription.
+ *
+ * <p>This value identifies whether the subscription supports various sms services.
+ * These services can include circuit-switched (CS) SMS, packet-switched (PS) IMS (IP
+ * Multimedia Subsystem) SMS, and over-the-top (OTT) SMS options.
+ *
+ * <p>Note: The availability of emergency SMS services is not solely dependent on this
+ * sms capability. Emergency services may be accessible even if the subscription lacks
+ * standard sms capabilities. However, the device's ability to support emergency sms
+ * can be influenced by its inherent sms capabilities, as determined by
+ * {@link TelephonyManager#isDeviceSmsCapable()}.
+ *
+ * @see TelephonyManager#isDeviceSmsCapable()
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final int SERVICE_CAPABILITY_SMS = 2;
+
+ /**
+ * Represents a value indicating the data calling capabilities of a subscription.
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final int SERVICE_CAPABILITY_DATA = 3;
+
+ /**
+ * Maximum value of service capabilities supported so far.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;
+
+ /**
+ * Bitmask for {@code SERVICE_CAPABILITY_VOICE}.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_VOICE_BITMASK =
+ serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);
+
+ /**
+ * Bitmask for {@code SERVICE_CAPABILITY_SMS}.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_SMS_BITMASK =
+ serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);
+
+ /**
+ * Bitmask for {@code SERVICE_CAPABILITY_DATA}.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_DATA_BITMASK =
+ serviceCapabilityToBitmask(SERVICE_CAPABILITY_DATA);
+
private final Context mContext;
/**
@@ -4484,4 +4574,38 @@
}
return new ArrayList<>();
}
+
+ /**
+ * @return the bitmasks combination of all service capabilities.
+ * @hide
+ */
+ public static int getAllServiceCapabilityBitmasks() {
+ return SERVICE_CAPABILITY_VOICE_BITMASK | SERVICE_CAPABILITY_SMS_BITMASK
+ | SERVICE_CAPABILITY_DATA_BITMASK;
+ }
+
+ /**
+ * @return The set of service capability from a bitmask combined one.
+ * @hide
+ */
+ @NonNull
+ @ServiceCapability
+ public static Set<Integer> getServiceCapabilitiesSet(int combinedServiceCapabilities) {
+ Set<Integer> capabilities = new HashSet<>();
+ for (int i = SERVICE_CAPABILITY_VOICE; i <= SERVICE_CAPABILITY_MAX; i++) {
+ final int capabilityBitmask = serviceCapabilityToBitmask(i);
+ if ((combinedServiceCapabilities & capabilityBitmask) == capabilityBitmask) {
+ capabilities.add(i);
+ }
+ }
+ return Collections.unmodifiableSet(capabilities);
+ }
+
+ /**
+ * @return The service capability bitmask from a {@link ServiceCapability} value.
+ * @hide
+ */
+ public static int serviceCapabilityToBitmask(@ServiceCapability int capability) {
+ return 1 << (capability - 1);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f8166e5..22a5cd7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6683,8 +6683,10 @@
* PackageManager.FEATURE_TELEPHONY system feature, which is available
* on any device with a telephony radio, even if the device is
* data-only.
+ * @deprecated Replaced by {@link #isDeviceVoiceCapable()}
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+ @Deprecated
public boolean isVoiceCapable() {
if (mContext == null) return true;
return mContext.getResources().getBoolean(
@@ -6692,6 +6694,30 @@
}
/**
+ * @return true if the current device is "voice capable".
+ * <p>
+ * "Voice capable" means that this device supports circuit-switched or IMS packet switched
+ * (i.e. voice) phone calls over the telephony network, and is allowed to display the in-call
+ * UI while a cellular voice call is active. This will be false on "data only" devices which
+ * can't make voice calls and don't support any in-call UI.
+ * <p>
+ * Note: the meaning of this flag is subtly different from the PackageManager
+ * .FEATURE_TELEPHONY system feature, which is available on any device with a telephony
+ * radio, even if the device is data-only.
+ * <p>
+ * To check if a subscription is "voice capable", call method
+ * {@link SubscriptionInfo#getServiceCapabilities()} and compare the result with
+ * bitmask {@link SubscriptionManager#SERVICE_CAPABILITY_VOICE}.
+ *
+ * @see SubscriptionInfo#getServiceCapabilities()
+ */
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public boolean isDeviceVoiceCapable() {
+ return isVoiceCapable();
+ }
+
+ /**
* @return true if the current device supports sms service.
* <p>
* If true, this means that the device supports both sending and
@@ -6699,6 +6725,7 @@
* <p>
* Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
* disabled when device doesn't support sms.
+ * @deprecated Replaced by {@link #isDeviceSmsCapable()}
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public boolean isSmsCapable() {
@@ -6708,6 +6735,27 @@
}
/**
+ * @return true if the current device supports SMS service.
+ * <p>
+ * If true, this means that the device supports both sending and
+ * receiving SMS via the telephony network.
+ * <p>
+ * Note: Voicemail waiting SMS, cell broadcasting SMS, and MMS are
+ * disabled when device doesn't support SMS.
+ * <p>
+ * To check if a subscription is "SMS capable", call method
+ * {@link SubscriptionInfo#getServiceCapabilities()} and compare result with
+ * bitmask {@link SubscriptionManager#SERVICE_CAPABILITY_SMS}.
+ *
+ * @see SubscriptionInfo#getServiceCapabilities()
+ */
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public boolean isDeviceSmsCapable() {
+ return isSmsCapable();
+ }
+
+ /**
* Requests all available cell information from all radios on the device including the
* camped/registered, serving, and neighboring cells.
*