summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/TEST_MAPPING4
-rw-r--r--core/java/android/util/FeatureFlagUtils.java4
-rw-r--r--core/res/TEST_MAPPING6
-rw-r--r--data/etc/TEST_MAPPING4
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml11
-rw-r--r--libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml21
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml93
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml (renamed from libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml)23
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java760
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java195
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt28
-rw-r--r--packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml25
-rw-r--r--packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml2
-rw-r--r--packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml2
-rw-r--r--packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml25
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_device_other.xml2
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_glasses.xml2
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_watch.xml2
-rw-r--r--packages/CompanionDeviceManager/res/layout/activity_confirmation.xml3
-rw-r--r--packages/CompanionDeviceManager/res/layout/list_item_device.xml1
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java7
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java5
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java15
-rw-r--r--packages/SystemUI/Android.bp11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt7
-rw-r--r--services/core/java/com/android/server/MmsServiceBroker.java6
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java2
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/TEST_MAPPING4
-rw-r--r--services/core/java/com/android/server/policy/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java40
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java36
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java4
-rw-r--r--telephony/common/com/android/internal/telephony/util/TelephonyUtils.java53
55 files changed, 1524 insertions, 380 deletions
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 0bdc222f323c..1df860258d82 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -42,10 +42,10 @@
},
{
"file_patterns": ["(/|^)AppOpsManager.java"],
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ "include-filter": "android.permissionpolicy.cts.RuntimePermissionProperties"
}
]
},
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6201b3a91eff..4c6bd671df7b 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -234,8 +234,8 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true");
- DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "false");
- DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
+ DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/res/TEST_MAPPING b/core/res/TEST_MAPPING
index 9185bae4e992..4d09076779a3 100644
--- a/core/res/TEST_MAPPING
+++ b/core/res/TEST_MAPPING
@@ -1,13 +1,13 @@
{
"presubmit": [
{
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.PermissionPolicyTest#platformPermissionPolicyIsUnaltered"
+ "include-filter": "android.permissionpolicy.cts.PermissionPolicyTest#platformPermissionPolicyIsUnaltered"
},
{
- "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ "include-filter": "android.permissionpolicy.cts.RuntimePermissionProperties"
}
]
}
diff --git a/data/etc/TEST_MAPPING b/data/etc/TEST_MAPPING
index 1a5db2f192cf..59277203ea71 100644
--- a/data/etc/TEST_MAPPING
+++ b/data/etc/TEST_MAPPING
@@ -2,10 +2,10 @@
"presubmit": [
{
"file_patterns": ["(/|^)platform.xml"],
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ "include-filter": "android.permissionpolicy.cts.RuntimePermissionProperties"
}
]
}
diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
index 27e0b184f427..5d7771366bec 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
@@ -14,13 +14,12 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="@color/decor_button_dark_color">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<group android:translateY="8.0">
<path
- android:fillColor="@android:color/white" android:pathData="M3,5V3H21V5Z"/>
+ android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
new file mode 100644
index 000000000000..3e0297ab612b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_baseline_expand_more_24.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+<vector android:height="24dp" android:tint="#000000"
+ android:viewportHeight="24" android:viewportWidth="24"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/black" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
new file mode 100644
index 000000000000..35562b650994
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/desktop_mode_caption"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:background="@drawable/desktop_mode_decor_title">
+
+ <LinearLayout
+ android:id="@+id/open_menu_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clickable="true"
+ android:focusable="true"
+ android:paddingStart="8dp"
+ android:background="?android:selectableItemBackgroundBorderless">
+
+ <ImageView
+ android:id="@+id/application_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_margin="4dp"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/app_icon_text" />
+
+ <TextView
+ android:id="@+id/application_name"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:minWidth="80dp"
+ android:textColor="@color/desktop_mode_caption_app_name_dark"
+ android:textSize="14sp"
+ android:textFontWeight="500"
+ android:gravity="center_vertical"
+ android:layout_weight="1"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ tools:text="Gmail"/>
+
+ <ImageButton
+ android:id="@+id/expand_menu_button"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:padding="4dp"
+ android:contentDescription="@string/collapse_menu_text"
+ android:src="@drawable/ic_baseline_expand_more_24"
+ android:tint="@color/desktop_mode_caption_expand_button_dark"
+ android:background="@null"
+ android:scaleType="fitCenter"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/caption_handle"
+ android:layout_width="wrap_content"
+ android:layout_height="40dp"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/close_window"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="4dp"
+ android:layout_marginEnd="8dp"
+ android:contentDescription="@string/close_button_text"
+ android:src="@drawable/decor_close_button_dark"
+ android:scaleType="fitCenter"
+ android:gravity="end"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:tint="@color/desktop_mode_caption_close_button_dark"/>
+</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
index f9aeb6a8448a..ac13eaeda6f5 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
@@ -50,7 +50,7 @@
android:layout_marginEnd="10dp"
android:contentDescription="@string/collapse_menu_text"
android:layout_alignParentEnd="true"
- android:background="@drawable/caption_collapse_menu_button"
+ android:background="@drawable/ic_baseline_expand_more_24"
android:layout_centerVertical="true"/>
</RelativeLayout>
<LinearLayout
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index 29cf1512e2e5..5ab159cdf264 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -16,26 +16,21 @@
-->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/desktop_mode_caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="@drawable/desktop_mode_decor_title">
- <Button
- style="@style/CaptionButtonStyle"
- android:id="@+id/back_button"
- android:contentDescription="@string/back_button_text"
- android:background="@drawable/decor_back_button_dark"/>
- <Button
+
+ <ImageButton
android:id="@+id/caption_handle"
android:layout_width="128dp"
- android:layout_height="32dp"
- android:layout_margin="5dp"
+ android:layout_height="42dp"
android:contentDescription="@string/handle_text"
- android:background="@drawable/decor_handle_dark"/>
- <Button
- style="@style/CaptionButtonStyle"
- android:id="@+id/close_window"
- android:contentDescription="@string/close_button_text"
- android:background="@drawable/decor_close_button_dark"/>
+ android:src="@drawable/decor_handle_dark"
+ tools:tint="@color/desktop_mode_caption_handle_bar_dark"
+ android:scaleType="fitXY"
+ android:background="?android:selectableItemBackground"/>
+
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 6fb70006e67f..2a03b03d86f1 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -54,4 +54,14 @@
<color name="splash_screen_bg_light">#FFFFFF</color>
<color name="splash_screen_bg_dark">#000000</color>
<color name="splash_window_background_default">@color/splash_screen_bg_light</color>
+
+ <!-- Desktop Mode -->
+ <color name="desktop_mode_caption_handle_bar_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_handle_bar_dark">#1C1C17</color>
+ <color name="desktop_mode_caption_expand_button_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_expand_button_dark">#48473A</color>
+ <color name="desktop_mode_caption_close_button_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_close_button_dark">#1C1C17</color>
+ <color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 3d5230d5cf90..57b5b8f24fad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -82,6 +82,7 @@ import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -520,6 +521,9 @@ public abstract class WMShellBaseModule {
desktopModeTaskRepository, mainExecutor));
}
+ @BindsOptionalOf
+ abstract RecentsTransitionHandler optionalRecentsTransitionHandler();
+
//
// Shell transitions
//
@@ -803,6 +807,7 @@ public abstract class WMShellBaseModule {
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformComponents> freeformComponents,
Optional<RecentTasksController> recentTasksOptional,
+ Optional<RecentsTransitionHandler> recentsTransitionHandlerOptional,
Optional<OneHandedController> oneHandedControllerOptional,
Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
Optional<ActivityEmbeddingController> activityEmbeddingOptional,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 7a83d101578f..cc0da2840fa0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -83,6 +83,7 @@ import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -528,9 +529,20 @@ public abstract class WMShellModule {
ShellInit shellInit,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
+ Optional<RecentsTransitionHandler> recentsTransitionHandler,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
- pipTouchHandlerOptional);
+ pipTouchHandlerOptional, recentsTransitionHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static RecentsTransitionHandler provideRecentsTransitionHandler(
+ ShellInit shellInit,
+ Transitions transitions,
+ Optional<RecentTasksController> recentTasksController) {
+ return new RecentsTransitionHandler(shellInit, transitions,
+ recentTasksController.orElse(null));
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index 1a6c1d65db03..4048c5b8feab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -17,6 +17,11 @@
package com.android.wm.shell.recents;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.IRecentsAnimationRunner;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -45,4 +50,10 @@ interface IRecentTasks {
* Gets the set of running tasks.
*/
RunningTaskInfo[] getRunningTasks(int maxNum) = 4;
+
+ /**
+ * Starts a recents transition.
+ */
+ oneway void startRecentsTransition(in PendingIntent intent, in Intent fillIn, in Bundle options,
+ IApplicationThread appThread, IRecentsAnimationRunner listener) = 5;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 0d9faa3c6f83..c5bfd8753994 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -24,13 +24,18 @@ import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RE
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.IRecentsAnimationRunner;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -79,6 +84,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasksImpl mImpl = new RecentTasksImpl();
private final ActivityTaskManager mActivityTaskManager;
+ private RecentsTransitionHandler mTransitionHandler = null;
private IRecentTasksListener mListener;
private final boolean mIsDesktopMode;
@@ -150,6 +156,10 @@ public class RecentTasksController implements TaskStackListenerCallback,
mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this));
}
+ void setTransitionHandler(RecentsTransitionHandler handler) {
+ mTransitionHandler = handler;
+ }
+
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
@@ -492,5 +502,18 @@ public class RecentTasksController implements TaskStackListenerCallback,
true /* blocking */);
return tasks[0];
}
+
+ @Override
+ public void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
+ IApplicationThread appThread, IRecentsAnimationRunner listener) {
+ if (mController.mTransitionHandler == null) {
+ Slog.e(TAG, "Used shell-transitions startRecentsTransition without"
+ + " shell-transitions");
+ return;
+ }
+ executeRemoteCallWithTaskPermission(mController, "startRecentsTransition",
+ (controller) -> controller.mTransitionHandler.startRecentsTransition(
+ intent, fillIn, options, appThread, listener));
+ }
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
new file mode 100644
index 000000000000..2ec64b009b3b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -0,0 +1,760 @@
+/*
+ * 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.wm.shell.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_SLEEP;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.PictureInPictureSurfaceTransaction;
+import android.window.TaskSnapshot;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
+
+import java.util.ArrayList;
+
+/**
+ * Handles the Recents (overview) animation. Only one of these can run at a time. A recents
+ * transition must be created via {@link #startRecentsTransition}. Anything else will be ignored.
+ */
+public class RecentsTransitionHandler implements Transitions.TransitionHandler {
+ private static final String TAG = "RecentsTransitionHandler";
+
+ private final Transitions mTransitions;
+ private final ShellExecutor mExecutor;
+ private IApplicationThread mAnimApp = null;
+ private final ArrayList<RecentsController> mControllers = new ArrayList<>();
+
+ /**
+ * List of other handlers which might need to mix recents with other things. These are checked
+ * in the order they are added. Ideally there should only be one.
+ */
+ private final ArrayList<RecentsMixedHandler> mMixers = new ArrayList<>();
+
+ public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
+ @Nullable RecentTasksController recentTasksController) {
+ mTransitions = transitions;
+ mExecutor = transitions.getMainExecutor();
+ if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
+ if (recentTasksController == null) return;
+ shellInit.addInitCallback(() -> {
+ recentTasksController.setTransitionHandler(this);
+ transitions.addHandler(this);
+ }, this);
+ }
+
+ /** Register a mixer handler. {@see RecentsMixedHandler}*/
+ public void addMixer(RecentsMixedHandler mixer) {
+ mMixers.add(mixer);
+ }
+
+ /** Unregister a Mixed Handler */
+ public void removeMixer(RecentsMixedHandler mixer) {
+ mMixers.remove(mixer);
+ }
+
+ void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
+ IApplicationThread appThread, IRecentsAnimationRunner listener) {
+ // only care about latest one.
+ mAnimApp = appThread;
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.sendPendingIntent(intent, fillIn, options);
+ final RecentsController controller = new RecentsController(listener);
+ RecentsMixedHandler mixer = null;
+ Transitions.TransitionHandler mixedHandler = null;
+ for (int i = 0; i < mMixers.size(); ++i) {
+ mixedHandler = mMixers.get(i).handleRecentsRequest(wct);
+ if (mixedHandler != null) {
+ mixer = mMixers.get(i);
+ break;
+ }
+ }
+ final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
+ mixedHandler == null ? this : mixedHandler);
+ if (mixer != null) {
+ mixer.setRecentsTransition(transition);
+ }
+ if (transition == null) {
+ controller.cancel();
+ return;
+ }
+ controller.setTransition(transition);
+ mControllers.add(controller);
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(IBinder transition,
+ TransitionRequestInfo request) {
+ // do not directly handle requests. Only entry point should be via startRecentsTransition
+ return null;
+ }
+
+ private int findController(IBinder transition) {
+ for (int i = mControllers.size() - 1; i >= 0; --i) {
+ if (mControllers.get(i).mTransition == transition) return i;
+ }
+ return -1;
+ }
+
+ @Override
+ public boolean startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ Transitions.TransitionFinishCallback finishCallback) {
+ final int controllerIdx = findController(transition);
+ if (controllerIdx < 0) return false;
+ final RecentsController controller = mControllers.get(controllerIdx);
+ Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
+ mAnimApp = null;
+ if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ Transitions.TransitionFinishCallback finishCallback) {
+ final int targetIdx = findController(mergeTarget);
+ if (targetIdx < 0) return;
+ final RecentsController controller = mControllers.get(targetIdx);
+ controller.merge(info, t, finishCallback);
+ }
+
+ @Override
+ public void onTransitionConsumed(IBinder transition, boolean aborted,
+ SurfaceControl.Transaction finishTransaction) {
+ final int idx = findController(transition);
+ if (idx < 0) return;
+ mControllers.get(idx).cancel();
+ }
+
+ /** There is only one of these and it gets reset on finish. */
+ private class RecentsController extends IRecentsAnimationController.Stub {
+ private IRecentsAnimationRunner mListener;
+ private IBinder.DeathRecipient mDeathHandler;
+ private Transitions.TransitionFinishCallback mFinishCB = null;
+ private SurfaceControl.Transaction mFinishTransaction = null;
+
+ /**
+ * List of tasks that we are switching away from via this transition. Upon finish, these
+ * pausing tasks will become invisible.
+ * These need to be ordered since the order must be restored if there is no task-switch.
+ */
+ private ArrayList<TaskState> mPausingTasks = null;
+
+ /**
+ * List of tasks that we are switching to. Upon finish, these will remain visible and
+ * on top.
+ */
+ private ArrayList<TaskState> mOpeningTasks = null;
+
+ private WindowContainerToken mPipTask = null;
+ private WindowContainerToken mRecentsTask = null;
+ private int mRecentsTaskId = -1;
+ private TransitionInfo mInfo = null;
+ private boolean mOpeningSeparateHome = false;
+ private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
+ private PictureInPictureSurfaceTransaction mPipTransaction = null;
+ private IBinder mTransition = null;
+ private boolean mKeyguardLocked = false;
+ private boolean mWillFinishToHome = false;
+
+ /** The animation is idle, waiting for the user to choose a task to switch to. */
+ private static final int STATE_NORMAL = 0;
+
+ /** The user chose a new task to switch to and the animation is animating to it. */
+ private static final int STATE_NEW_TASK = 1;
+
+ /** The latest state that the recents animation is operating in. */
+ private int mState = STATE_NORMAL;
+
+ RecentsController(IRecentsAnimationRunner listener) {
+ mListener = listener;
+ mDeathHandler = () -> mExecutor.execute(() -> {
+ if (mListener == null) return;
+ if (mFinishCB != null) {
+ finish(mWillFinishToHome, false /* leaveHint */);
+ }
+ });
+ try {
+ mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
+ } catch (RemoteException e) {
+ mListener = null;
+ }
+ }
+
+ void setTransition(IBinder transition) {
+ mTransition = transition;
+ }
+
+ void cancel() {
+ // restoring (to-home = false) involves submitting more WM changes, so by default, use
+ // toHome = true when canceling.
+ cancel(true /* toHome */);
+ }
+
+ void cancel(boolean toHome) {
+ if (mListener != null) {
+ try {
+ mListener.onAnimationCanceled(null, null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error canceling recents animation", e);
+ }
+ }
+ if (mFinishCB != null) {
+ finish(toHome, false /* userLeave */);
+ } else {
+ cleanUp();
+ }
+ }
+
+ /**
+ * Sends a cancel message to the recents animation with snapshots. Used to trigger a
+ * "replace-with-screenshot" like behavior.
+ */
+ private boolean sendCancelWithSnapshots() {
+ int[] taskIds = null;
+ TaskSnapshot[] snapshots = null;
+ if (mPausingTasks.size() > 0) {
+ taskIds = new int[mPausingTasks.size()];
+ snapshots = new TaskSnapshot[mPausingTasks.size()];
+ try {
+ for (int i = 0; i < mPausingTasks.size(); ++i) {
+ snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot(
+ mPausingTasks.get(0).mTaskInfo.taskId);
+ }
+ } catch (RemoteException e) {
+ taskIds = null;
+ snapshots = null;
+ }
+ }
+ try {
+ mListener.onAnimationCanceled(taskIds, snapshots);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error canceling recents animation", e);
+ return false;
+ }
+ return true;
+ }
+
+ void cleanUp() {
+ if (mListener != null && mDeathHandler != null) {
+ mListener.asBinder().unlinkToDeath(mDeathHandler, 0 /* flags */);
+ mDeathHandler = null;
+ }
+ mListener = null;
+ mFinishCB = null;
+ // clean-up leash surfacecontrols and anything that might reference them.
+ if (mLeashMap != null) {
+ for (int i = 0; i < mLeashMap.size(); ++i) {
+ mLeashMap.valueAt(i).release();
+ }
+ mLeashMap = null;
+ }
+ mFinishTransaction = null;
+ mPausingTasks = null;
+ mOpeningTasks = null;
+ mInfo = null;
+ mTransition = null;
+ mControllers.remove(this);
+ }
+
+ boolean start(TransitionInfo info, SurfaceControl.Transaction t,
+ SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCB) {
+ if (mListener == null || mTransition == null) {
+ cleanUp();
+ return false;
+ }
+ // First see if this is a valid recents transition.
+ boolean hasPausingTasks = false;
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (TransitionUtil.isWallpaper(change)) continue;
+ if (TransitionUtil.isClosingType(change.getMode())) {
+ hasPausingTasks = true;
+ continue;
+ }
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
+ mRecentsTask = taskInfo.token;
+ mRecentsTaskId = taskInfo.taskId;
+ } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ mRecentsTask = taskInfo.token;
+ mRecentsTaskId = taskInfo.taskId;
+ }
+ }
+ if (mRecentsTask == null && !hasPausingTasks) {
+ // Recents is already running apparently, so this is a no-op.
+ Slog.e(TAG, "Tried to start recents while it is already running.");
+ cleanUp();
+ return false;
+ }
+
+ mInfo = info;
+ mFinishCB = finishCB;
+ mFinishTransaction = finishT;
+ mPausingTasks = new ArrayList<>();
+ mOpeningTasks = new ArrayList<>();
+ mLeashMap = new ArrayMap<>();
+ mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
+
+ final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
+ final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
+ TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
+ // About layering: we divide up the "layer space" into 3 regions (each the size of
+ // the change count). This lets us categorize things into above/below/between
+ // while maintaining their relative ordering.
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (TransitionUtil.isWallpaper(change)) {
+ final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
+ // wallpapers go into the "below" layer space
+ info.getChanges().size() - i, info, t, mLeashMap);
+ wallpapers.add(target);
+ // Make all the wallpapers opaque since we want them visible from the start
+ t.setAlpha(target.leash, 1);
+ } else if (leafTaskFilter.test(change)) {
+ // start by putting everything into the "below" layer space.
+ final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
+ info.getChanges().size() - i, info, t, mLeashMap);
+ apps.add(target);
+ if (TransitionUtil.isClosingType(change.getMode())) {
+ // raise closing (pausing) task to "above" layer so it isn't covered
+ t.setLayer(target.leash, info.getChanges().size() * 3 - i);
+ mPausingTasks.add(new TaskState(change, target.leash));
+ if (taskInfo.pictureInPictureParams != null
+ && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
+ mPipTask = taskInfo.token;
+ }
+ } else if (taskInfo != null
+ && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
+ // There's a 3p launcher, so make sure recents goes above that.
+ t.setLayer(target.leash, info.getChanges().size() * 3 - i);
+ } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ // do nothing
+ } else if (TransitionUtil.isOpeningType(change.getMode())) {
+ mOpeningTasks.add(new TaskState(change, target.leash));
+ }
+ }
+ }
+ t.apply();
+ try {
+ mListener.onAnimationStart(this,
+ apps.toArray(new RemoteAnimationTarget[apps.size()]),
+ wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
+ new Rect(0, 0, 0, 0), new Rect());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting recents animation", e);
+ cancel();
+ }
+ return true;
+ }
+
+ @SuppressLint("NewApi")
+ void merge(TransitionInfo info, SurfaceControl.Transaction t,
+ Transitions.TransitionFinishCallback finishCallback) {
+ if (mFinishCB == null) {
+ // This was no-op'd (likely a repeated start) and we've already sent finish.
+ return;
+ }
+ if (info.getType() == TRANSIT_SLEEP) {
+ // A sleep event means we need to stop animations immediately, so cancel here.
+ cancel();
+ return;
+ }
+ ArrayList<TransitionInfo.Change> openingTasks = null;
+ ArrayList<TransitionInfo.Change> closingTasks = null;
+ mOpeningSeparateHome = false;
+ TransitionInfo.Change recentsOpening = null;
+ boolean foundRecentsClosing = false;
+ boolean hasChangingApp = false;
+ final TransitionUtil.LeafTaskFilter leafTaskFilter =
+ new TransitionUtil.LeafTaskFilter();
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ final boolean isLeafTask = leafTaskFilter.test(change);
+ if (TransitionUtil.isOpeningType(change.getMode())) {
+ if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) {
+ recentsOpening = change;
+ } else if (isLeafTask) {
+ if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ // This is usually a 3p launcher
+ mOpeningSeparateHome = true;
+ }
+ if (openingTasks == null) {
+ openingTasks = new ArrayList<>();
+ }
+ openingTasks.add(change);
+ }
+ } else if (TransitionUtil.isClosingType(change.getMode())) {
+ if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) {
+ foundRecentsClosing = true;
+ } else if (isLeafTask) {
+ if (closingTasks == null) {
+ closingTasks = new ArrayList<>();
+ }
+ closingTasks.add(change);
+ }
+ } else if (change.getMode() == TRANSIT_CHANGE) {
+ // Finish recents animation if the display is changed, so the default
+ // transition handler can play the animation such as rotation effect.
+ if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) {
+ cancel(mWillFinishToHome);
+ return;
+ }
+ hasChangingApp = true;
+ }
+ }
+ if (hasChangingApp && foundRecentsClosing) {
+ // This happens when a visible app is expanding (usually PiP). In this case,
+ // that transition probably has a special-purpose animation, so finish recents
+ // now and let it do its animation (since recents is going to be occluded).
+ sendCancelWithSnapshots();
+ mExecutor.executeDelayed(
+ () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0);
+ return;
+ }
+ if (recentsOpening != null) {
+ // the recents task re-appeared. This happens if the user gestures before the
+ // task-switch (NEW_TASK) animation finishes.
+ if (mState == STATE_NORMAL) {
+ Slog.e(TAG, "Returning to recents while recents is already idle.");
+ }
+ if (closingTasks == null || closingTasks.size() == 0) {
+ Slog.e(TAG, "Returning to recents without closing any opening tasks.");
+ }
+ // Setup may hide it initially since it doesn't know that overview was still active.
+ t.show(recentsOpening.getLeash());
+ t.setAlpha(recentsOpening.getLeash(), 1.f);
+ mState = STATE_NORMAL;
+ }
+ boolean didMergeThings = false;
+ if (closingTasks != null) {
+ // Cancelling a task-switch. Move the tasks back to mPausing from mOpening
+ for (int i = 0; i < closingTasks.size(); ++i) {
+ final TransitionInfo.Change change = closingTasks.get(i);
+ int openingIdx = TaskState.indexOf(mOpeningTasks, change);
+ if (openingIdx < 0) {
+ Slog.e(TAG, "Back to existing recents animation from an unrecognized "
+ + "task: " + change.getTaskInfo().taskId);
+ continue;
+ }
+ mPausingTasks.add(mOpeningTasks.remove(openingIdx));
+ didMergeThings = true;
+ }
+ }
+ RemoteAnimationTarget[] appearedTargets = null;
+ if (openingTasks != null && openingTasks.size() > 0) {
+ // Switching to some new tasks, add to mOpening and remove from mPausing. Also,
+ // enter NEW_TASK state since this will start the switch-to animation.
+ final int layer = mInfo.getChanges().size() * 3;
+ appearedTargets = new RemoteAnimationTarget[openingTasks.size()];
+ for (int i = 0; i < openingTasks.size(); ++i) {
+ final TransitionInfo.Change change = openingTasks.get(i);
+ int pausingIdx = TaskState.indexOf(mPausingTasks, change);
+ if (pausingIdx >= 0) {
+ // Something is showing/opening a previously-pausing app.
+ appearedTargets[i] = TransitionUtil.newTarget(
+ change, layer, mPausingTasks.get(pausingIdx).mLeash);
+ mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
+ // Setup hides opening tasks initially, so make it visible again (since we
+ // are already showing it).
+ t.show(change.getLeash());
+ t.setAlpha(change.getLeash(), 1.f);
+ } else {
+ // We are receiving new opening tasks, so convert to onTasksAppeared.
+ appearedTargets[i] = TransitionUtil.newTarget(
+ change, layer, info, t, mLeashMap);
+ // reparent into the original `mInfo` since that's where we are animating.
+ final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+ t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash());
+ t.setLayer(appearedTargets[i].leash, layer);
+ mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash));
+ }
+ }
+ didMergeThings = true;
+ mState = STATE_NEW_TASK;
+ }
+ if (!didMergeThings) {
+ // Didn't recognize anything in incoming transition so don't merge it.
+ Slog.w(TAG, "Don't know how to merge this transition.");
+ return;
+ }
+ // At this point, we are accepting the merge.
+ t.apply();
+ // not using the incoming anim-only surfaces
+ info.releaseAnimSurfaces();
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ if (appearedTargets == null) return;
+ try {
+ mListener.onTasksAppeared(appearedTargets);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
+ }
+ }
+
+ @Override
+ public TaskSnapshot screenshotTask(int taskId) {
+ try {
+ return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to screenshot task", e);
+ }
+ return null;
+ }
+
+ @Override
+ public void setInputConsumerEnabled(boolean enabled) {
+ mExecutor.execute(() -> {
+ if (mFinishCB == null || !enabled) return;
+ // transient launches don't receive focus automatically. Since we are taking over
+ // the gesture now, take focus explicitly.
+ // This also moves recents back to top if the user gestured before a switch
+ // animation finished.
+ try {
+ ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set focused task", e);
+ }
+ });
+ }
+
+ @Override
+ public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
+ }
+
+ @Override
+ public void setFinishTaskTransaction(int taskId,
+ PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
+ mExecutor.execute(() -> {
+ if (mFinishCB == null) return;
+ mPipTransaction = finishTransaction;
+ });
+ }
+
+ @Override
+ @SuppressLint("NewApi")
+ public void finish(boolean toHome, boolean sendUserLeaveHint) {
+ mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint));
+ }
+
+ private void finishInner(boolean toHome, boolean sendUserLeaveHint) {
+ if (mFinishCB == null) {
+ Slog.e(TAG, "Duplicate call to finish");
+ return;
+ }
+ final Transitions.TransitionFinishCallback finishCB = mFinishCB;
+ mFinishCB = null;
+
+ final SurfaceControl.Transaction t = mFinishTransaction;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ if (mKeyguardLocked && mRecentsTask != null) {
+ if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
+ else wct.restoreTransientOrder(mRecentsTask);
+ }
+ if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+ // The gesture is returning to the pausing-task(s) rather than continuing with
+ // recents, so end the transition by moving the app back to the top (and also
+ // re-showing it's task).
+ for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+ // reverse order so that index 0 ends up on top
+ wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */);
+ t.show(mPausingTasks.get(i).mTaskSurface);
+ }
+ if (!mKeyguardLocked && mRecentsTask != null) {
+ wct.restoreTransientOrder(mRecentsTask);
+ }
+ } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
+ // Special situation where 3p launcher was changed during recents (this happens
+ // during tapltests...). Here we get both "return to home" AND "home opening".
+ // This is basically going home, but we have to restore the recents and home order.
+ for (int i = 0; i < mOpeningTasks.size(); ++i) {
+ final TaskState state = mOpeningTasks.get(i);
+ if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ // Make sure it is on top.
+ wct.reorder(state.mToken, true /* onTop */);
+ }
+ t.show(state.mTaskSurface);
+ }
+ for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+ t.hide(mPausingTasks.get(i).mTaskSurface);
+ }
+ if (!mKeyguardLocked && mRecentsTask != null) {
+ wct.restoreTransientOrder(mRecentsTask);
+ }
+ } else {
+ // The general case: committing to recents, going home, or switching tasks.
+ for (int i = 0; i < mOpeningTasks.size(); ++i) {
+ t.show(mOpeningTasks.get(i).mTaskSurface);
+ }
+ for (int i = 0; i < mPausingTasks.size(); ++i) {
+ if (!sendUserLeaveHint) {
+ // This means recents is not *actually* finishing, so of course we gotta
+ // do special stuff in WMCore to accommodate.
+ wct.setDoNotPip(mPausingTasks.get(i).mToken);
+ }
+ // Since we will reparent out of the leashes, pre-emptively hide the child
+ // surface to match the leash. Otherwise, there will be a flicker before the
+ // visibility gets committed in Core when using split-screen (in splitscreen,
+ // the leaf-tasks are not "independent" so aren't hidden by normal setup).
+ t.hide(mPausingTasks.get(i).mTaskSurface);
+ }
+ if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) {
+ t.show(mInfo.getChange(mPipTask).getLeash());
+ PictureInPictureSurfaceTransaction.apply(mPipTransaction,
+ mInfo.getChange(mPipTask).getLeash(), t);
+ mPipTask = null;
+ mPipTransaction = null;
+ }
+ }
+ cleanUp();
+ finishCB.onTransitionFinished(wct.isEmpty() ? null : wct, null /* wctCB */);
+ }
+
+ @Override
+ public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
+ }
+
+ @Override
+ public void cleanupScreenshot() {
+ }
+
+ @Override
+ public void setWillFinishToHome(boolean willFinishToHome) {
+ mExecutor.execute(() -> {
+ mWillFinishToHome = willFinishToHome;
+ });
+ }
+
+ /**
+ * @see IRecentsAnimationController#removeTask
+ */
+ @Override
+ public boolean removeTask(int taskId) {
+ return false;
+ }
+
+ /**
+ * @see IRecentsAnimationController#detachNavigationBarFromApp
+ */
+ @Override
+ public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+ mExecutor.execute(() -> {
+ if (mTransition == null) return;
+ try {
+ ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to detach the navigation bar from app", e);
+ }
+ });
+ }
+
+ /**
+ * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+ */
+ @Override
+ public void animateNavigationBarToApp(long duration) {
+ }
+ };
+
+ /** Utility class to track the state of a task as-seen by recents. */
+ private static class TaskState {
+ WindowContainerToken mToken;
+ ActivityManager.RunningTaskInfo mTaskInfo;
+
+ /** The surface/leash of the task provided by Core. */
+ SurfaceControl mTaskSurface;
+
+ /** The (local) animation-leash created for this task. */
+ SurfaceControl mLeash;
+
+ TaskState(TransitionInfo.Change change, SurfaceControl leash) {
+ mToken = change.getContainer();
+ mTaskInfo = change.getTaskInfo();
+ mTaskSurface = change.getLeash();
+ mLeash = leash;
+ }
+
+ static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) {
+ for (int i = list.size() - 1; i >= 0; --i) {
+ if (list.get(i).mToken.equals(change.getContainer())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public String toString() {
+ return "" + mToken + " : " + mLeash;
+ }
+ }
+
+ /**
+ * An interface for a mixed handler to receive information about recents requests (since these
+ * come into this handler directly vs from WMCore request).
+ */
+ public interface RecentsMixedHandler {
+ /**
+ * Called when a recents request comes in. The handler can add operations to outWCT. If
+ * the handler wants to "accept" the transition, it should return itself; otherwise, it
+ * should return `null`.
+ *
+ * If a mixed-handler accepts this recents, it will be the de-facto handler for this
+ * transition and is required to call the associated {@link #startAnimation},
+ * {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
+ */
+ Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT);
+
+ /**
+ * Reports the transition token associated with the accepted recents request. If there was
+ * a problem starting the request, this will be called with `null`.
+ */
+ void setRecentsTransition(@Nullable IBinder transition);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index aa851d1bd30f..586cab07c508 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -43,6 +43,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellInit;
@@ -55,10 +56,12 @@ import java.util.Optional;
* A handler for dealing with transitions involving multiple other handlers. For example: an
* activity in split-screen going into PiP.
*/
-public class DefaultMixedHandler implements Transitions.TransitionHandler {
+public class DefaultMixedHandler implements Transitions.TransitionHandler,
+ RecentsTransitionHandler.RecentsMixedHandler {
private final Transitions mPlayer;
private PipTransitionController mPipHandler;
+ private RecentsTransitionHandler mRecentsHandler;
private StageCoordinator mSplitHandler;
private static class MixedTransition {
@@ -122,7 +125,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
Optional<SplitScreenController> splitScreenControllerOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional) {
+ Optional<PipTouchHandler> pipTouchHandlerOptional,
+ Optional<RecentsTransitionHandler> recentsHandlerOptional) {
mPlayer = player;
if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
&& splitScreenControllerOptional.isPresent()) {
@@ -134,6 +138,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
if (mSplitHandler != null) {
mSplitHandler.setMixedHandler(this);
}
+ mRecentsHandler = recentsHandlerOptional.orElse(null);
+ if (mRecentsHandler != null) {
+ mRecentsHandler.addMixer(this);
+ }
}, this);
}
}
@@ -200,6 +208,29 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
return null;
}
+ @Override
+ public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
+ if (mRecentsHandler != null && mSplitHandler.isSplitActive()) {
+ return this;
+ }
+ return null;
+ }
+
+ @Override
+ public void setRecentsTransition(IBinder transition) {
+ if (mSplitHandler.isSplitActive()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ + "Split-Screen is active, so treat it as Mixed.");
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
+ mixed.mLeftoversHandler = mRecentsHandler;
+ mActiveTransitions.add(mixed);
+ } else {
+ throw new IllegalStateException("Accepted a recents transition but don't know how to"
+ + " handle it");
+ }
+ }
+
private TransitionInfo subCopy(@NonNull TransitionInfo info,
@WindowManager.TransitionType int newType, boolean withChanges) {
final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
@@ -561,6 +592,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
mPipHandler.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
+ mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 6b45149e35a2..d2a80471a46f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -166,7 +166,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
decoration.relayout(taskInfo);
- setupCaptionColor(taskInfo, decoration);
}
@Override
@@ -252,7 +251,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
- } else if (id == R.id.caption_handle) {
+ } else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
decoration.createHandleMenu();
} else if (id == R.id.desktop_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
@@ -262,7 +261,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu();
- decoration.setButtonVisibility(false);
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
}
@@ -556,13 +554,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
- private void setupCaptionColor(RunningTaskInfo taskInfo,
- DesktopModeWindowDecoration decoration) {
- if (taskInfo == null || taskInfo.taskDescription == null) return;
- final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
- decoration.setCaptionColor(statusBarColor);
- }
-
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.isProto2Enabled()
@@ -602,7 +593,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
- setupCaptionColor(taskInfo, windowDecoration);
incrementEventReceiverTasks(taskInfo.displayId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 3c0ef965f4f5..43605e30b813 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -22,15 +22,10 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.util.Log;
import android.view.Choreographer;
@@ -48,20 +43,24 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
+import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
+import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link DesktopModeWindowDecorViewModel}. The caption bar contains a handle, back button, and
- * close button.
+ * {@link DesktopModeWindowDecorViewModel}.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private static final String TAG = "DesktopModeWindowDecoration";
+
private final Handler mHandler;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
+ private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private DragPositioningCallback mDragPositioningCallback;
@@ -73,7 +72,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
- private boolean mDesktopActive;
private AdditionalWindow mHandleMenu;
private final int mHandleMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
private final int mHandleMenuShadowRadiusId = R.dimen.caption_menu_shadow_radius;
@@ -94,7 +92,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
- mDesktopActive = DesktopModeStatus.isActive(mContext);
}
@Override
@@ -152,9 +149,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int outsetRightId = R.dimen.freeform_resize_handle;
final int outsetBottomId = R.dimen.freeform_resize_handle;
+ final int windowDecorLayoutId = getDesktopModeWindowDecorLayoutId(
+ taskInfo.getWindowingMode());
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
- mRelayoutParams.mLayoutResId = R.layout.desktop_mode_window_decor;
+ mRelayoutParams.mLayoutResId = windowDecorLayoutId;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
if (isDragResizeable) {
@@ -172,23 +171,27 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return;
}
if (oldRootView != mResult.mRootView) {
- setupRootView();
+ if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) {
+ mWindowDecorViewHolder = new DesktopModeFocusedWindowDecorationViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener
+ );
+ } else if (mRelayoutParams.mLayoutResId
+ == R.layout.desktop_mode_app_controls_window_decor) {
+ mWindowDecorViewHolder = new DesktopModeAppControlsWindowDecorationViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener
+ );
+ } else {
+ throw new IllegalArgumentException("Unexpected layout resource id");
+ }
}
+ mWindowDecorViewHolder.bindData(mTaskInfo);
- // If this task is not focused, do not show caption.
- setCaptionVisibility(mTaskInfo.isFocused);
-
- if (mTaskInfo.isFocused) {
- if (DesktopModeStatus.isProto2Enabled()) {
- updateButtonVisibility();
- } else if (DesktopModeStatus.isProto1Enabled()) {
- // Only handle should show if Desktop Mode is inactive.
- boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext);
- if (mDesktopActive != desktopCurrentStatus) {
- mDesktopActive = desktopCurrentStatus;
- setButtonVisibility(mDesktopActive);
- }
- }
+ if (!mTaskInfo.isFocused) {
+ closeHandleMenu();
}
if (!isDragResizeable) {
@@ -219,24 +222,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
}
- /**
- * Sets up listeners when a new root view is created.
- */
- private void setupRootView() {
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- caption.setOnTouchListener(mOnCaptionTouchListener);
- final View handle = caption.findViewById(R.id.caption_handle);
- handle.setOnTouchListener(mOnCaptionTouchListener);
- handle.setOnClickListener(mOnCaptionButtonClickListener);
- if (DesktopModeStatus.isProto1Enabled()) {
- final View back = caption.findViewById(R.id.back_button);
- back.setOnClickListener(mOnCaptionButtonClickListener);
- final View close = caption.findViewById(R.id.close_window);
- close.setOnClickListener(mOnCaptionButtonClickListener);
- }
- updateButtonVisibility();
- }
-
private void setupHandleMenu() {
final View menu = mHandleMenu.mWindowViewHost.getView();
final View fullscreen = menu.findViewById(R.id.fullscreen_button);
@@ -255,98 +240,26 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
collapse.setOnClickListener(mOnCaptionButtonClickListener);
menu.setOnTouchListener(mOnCaptionTouchListener);
- String packageName = mTaskInfo.baseActivity.getPackageName();
- PackageManager pm = mContext.getApplicationContext().getPackageManager();
- // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
- try {
- ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
- PackageManager.ApplicationInfoFlags.of(0));
- final ImageView appIcon = menu.findViewById(R.id.application_icon);
- appIcon.setImageDrawable(pm.getApplicationIcon(applicationInfo));
- final TextView appName = menu.findViewById(R.id.application_name);
- appName.setText(pm.getApplicationLabel(applicationInfo));
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Package not found: " + packageName, e);
- }
- }
-
- /**
- * Sets caption visibility based on task focus.
- * Note: Only applicable to Desktop Proto 1; Proto 2 only closes handle menu on focus loss
- * @param visible whether or not the caption should be visible
- */
- private void setCaptionVisibility(boolean visible) {
- if (!visible) closeHandleMenu();
- if (!DesktopModeStatus.isProto1Enabled()) return;
- final int v = visible ? View.VISIBLE : View.GONE;
- final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- captionView.setVisibility(v);
-
- }
-
- /**
- * Sets the visibility of buttons and color of caption based on desktop mode status
- */
- void updateButtonVisibility() {
- if (DesktopModeStatus.isProto2Enabled()) {
- setButtonVisibility(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM);
- } else if (DesktopModeStatus.isProto1Enabled()) {
- mDesktopActive = DesktopModeStatus.isActive(mContext);
- setButtonVisibility(mDesktopActive);
- }
- }
-
- /**
- * Show or hide buttons
- */
- void setButtonVisibility(boolean visible) {
- final int visibility = visible && DesktopModeStatus.isProto1Enabled()
- ? View.VISIBLE : View.GONE;
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- final View back = caption.findViewById(R.id.back_button);
- final View close = caption.findViewById(R.id.close_window);
- back.setVisibility(visibility);
- close.setVisibility(visibility);
- final int buttonTintColorRes =
- mDesktopActive ? R.color.decor_button_dark_color
- : R.color.decor_button_light_color;
- final ColorStateList buttonTintColor =
- caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
- final View handle = caption.findViewById(R.id.caption_handle);
- final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
- handleBackground.setTintList(buttonTintColor);
+ final ImageView appIcon = menu.findViewById(R.id.application_icon);
+ final TextView appName = menu.findViewById(R.id.application_name);
+ loadAppInfo(appName, appIcon);
}
boolean isHandleMenuActive() {
return mHandleMenu != null;
}
- void setCaptionColor(int captionColor) {
- if (mResult.mRootView == null) {
- return;
- }
-
- final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
- final GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
- captionDrawable.setColor(captionColor);
-
- final int buttonTintColorRes =
- Color.valueOf(captionColor).luminance() < 0.5
- ? R.color.decor_button_light_color
- : R.color.decor_button_dark_color;
- final ColorStateList buttonTintColor =
- caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
-
- final View handle = caption.findViewById(R.id.caption_handle);
- final Drawable handleBackground = handle.getBackground();
- handleBackground.setTintList(buttonTintColor);
- if (DesktopModeStatus.isProto1Enabled()) {
- final View back = caption.findViewById(R.id.back_button);
- final Drawable backBackground = back.getBackground();
- backBackground.setTintList(buttonTintColor);
- final View close = caption.findViewById(R.id.close_window);
- final Drawable closeBackground = close.getBackground();
- closeBackground.setTintList(buttonTintColor);
+ private void loadAppInfo(TextView appNameTextView, ImageView appIconImageView) {
+ String packageName = mTaskInfo.realActivity.getPackageName();
+ PackageManager pm = mContext.getApplicationContext().getPackageManager();
+ try {
+ // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(0));
+ appNameTextView.setText(pm.getApplicationLabel(applicationInfo));
+ appIconImageView.setImageDrawable(pm.getApplicationIcon(applicationInfo));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Package not found: " + packageName, e);
}
}
@@ -371,9 +284,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int shadowRadius = loadDimensionPixelSize(resources, mHandleMenuShadowRadiusId);
final int cornerRadius = loadDimensionPixelSize(resources, mHandleMenuCornerRadiusId);
- final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
- - mResult.mDecorContainerOffsetX;
- final int y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+ final int x, y;
+ if (mRelayoutParams.mLayoutResId
+ == R.layout.desktop_mode_app_controls_window_decor) {
+ // Align the handle menu to the left of the caption.
+ x = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX;
+ y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+ } else {
+ // Position the handle menu at the center of the caption.
+ x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
+ - mResult.mDecorContainerOffsetX;
+ y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
+ }
mHandleMenuPosition.set(x, y);
String namePrefix = "Caption Menu";
mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
@@ -503,6 +425,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
super.close();
}
+ private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
+ if (DesktopModeStatus.isProto1Enabled()) {
+ return R.layout.desktop_mode_app_controls_window_decor;
+ }
+ return windowingMode == WINDOWING_MODE_FREEFORM
+ ? R.layout.desktop_mode_app_controls_window_decor
+ : R.layout.desktop_mode_focused_window_decor;
+ }
+
static class Factory {
DesktopModeWindowDecoration create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index ddd3b440e1a6..31e93ac6068f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -84,6 +84,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
};
RunningTaskInfo mTaskInfo;
+ int mLayoutResId;
final SurfaceControl mTaskSurface;
Display mDisplay;
@@ -162,6 +163,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
}
+ final int oldLayoutResId = mLayoutResId;
+ mLayoutResId = params.mLayoutResId;
if (!mTaskInfo.isVisible) {
releaseViews();
@@ -178,7 +181,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo);
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|| mDisplay == null
- || mDisplay.getDisplayId() != mTaskInfo.displayId) {
+ || mDisplay.getDisplayId() != mTaskInfo.displayId
+ || oldLayoutResId != mLayoutResId) {
releaseViews();
if (!obtainDisplayOrRegisterListener()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
new file mode 100644
index 000000000000..95b5051cb81d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -0,0 +1,94 @@
+package com.android.wm.shell.windowdecor.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.pm.PackageManager
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
+import android.util.Log
+import android.view.View
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.wm.shell.R
+
+/**
+ * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
+ * finer controls such as a close window button and an "app info" section to pull up additional
+ * controls.
+ */
+internal class DesktopModeAppControlsWindowDecorationViewHolder(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: View.OnClickListener
+) : DesktopModeWindowDecorationViewHolder(rootView) {
+
+ private val captionView: View = rootView.findViewById(R.id.desktop_mode_caption)
+ private val captionHandle: View = rootView.findViewById(R.id.caption_handle)
+ private val openMenuButton: View = rootView.findViewById(R.id.open_menu_button)
+ private val closeWindowButton: ImageButton = rootView.findViewById(R.id.close_window)
+ private val expandMenuButton: ImageButton = rootView.findViewById(R.id.expand_menu_button)
+ private val appNameTextView: TextView = rootView.findViewById(R.id.application_name)
+ private val appIconImageView: ImageView = rootView.findViewById(R.id.application_icon)
+
+ init {
+ captionView.setOnTouchListener(onCaptionTouchListener)
+ captionHandle.setOnTouchListener(onCaptionTouchListener)
+ openMenuButton.setOnClickListener(onCaptionButtonClickListener)
+ closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+ }
+
+ override fun bindData(taskInfo: RunningTaskInfo) {
+ bindAppInfo(taskInfo)
+
+ val captionDrawable = captionView.background as GradientDrawable
+ captionDrawable.setColor(taskInfo.taskDescription.statusBarColor)
+
+ closeWindowButton.imageTintList = ColorStateList.valueOf(
+ getCaptionCloseButtonColor(taskInfo))
+ expandMenuButton.imageTintList = ColorStateList.valueOf(
+ getCaptionExpandButtonColor(taskInfo))
+ appNameTextView.setTextColor(getCaptionAppNameTextColor(taskInfo))
+ }
+
+ private fun bindAppInfo(taskInfo: RunningTaskInfo) {
+ val packageName: String = taskInfo.realActivity.packageName
+ val pm: PackageManager = context.applicationContext.packageManager
+ try {
+ // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
+ val applicationInfo = pm.getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(0))
+ appNameTextView.text = pm.getApplicationLabel(applicationInfo)
+ appIconImageView.setImageDrawable(pm.getApplicationIcon(applicationInfo))
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Package not found: $packageName", e)
+ }
+ }
+
+ private fun getCaptionAppNameTextColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_app_name_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_app_name_dark)
+ }
+ }
+
+ private fun getCaptionCloseButtonColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_close_button_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_close_button_dark)
+ }
+ }
+
+ private fun getCaptionExpandButtonColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_expand_button_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_expand_button_dark)
+ }
+ }
+
+ companion object {
+ private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
new file mode 100644
index 000000000000..47a12a0cb71c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -0,0 +1,44 @@
+package com.android.wm.shell.windowdecor.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.widget.ImageButton
+import com.android.wm.shell.R
+
+/**
+ * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen). It
+ * hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
+ */
+internal class DesktopModeFocusedWindowDecorationViewHolder(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: View.OnClickListener
+) : DesktopModeWindowDecorationViewHolder(rootView) {
+
+ private val captionView: View = rootView.findViewById(R.id.desktop_mode_caption)
+ private val captionHandle: ImageButton = rootView.findViewById(R.id.caption_handle)
+
+ init {
+ captionView.setOnTouchListener(onCaptionTouchListener)
+ captionHandle.setOnTouchListener(onCaptionTouchListener)
+ captionHandle.setOnClickListener(onCaptionButtonClickListener)
+ }
+
+ override fun bindData(taskInfo: RunningTaskInfo) {
+ val captionColor = taskInfo.taskDescription.statusBarColor
+ val captionDrawable = captionView.background as GradientDrawable
+ captionDrawable.setColor(captionColor)
+
+ captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
+ }
+
+ private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_handle_bar_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_handle_bar_dark)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
new file mode 100644
index 000000000000..514ea52cb8ae
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
@@ -0,0 +1,28 @@
+package com.android.wm.shell.windowdecor.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.graphics.Color
+import android.view.View
+
+/**
+ * Encapsulates the root [View] of a window decoration and its children to facilitate looking up
+ * children (via findViewById) and updating to the latest data from [RunningTaskInfo].
+ */
+internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) {
+ val context: Context = rootView.context
+
+ /**
+ * A signal to the view holder that new data is available and that the views should be updated
+ * to reflect it.
+ */
+ abstract fun bindData(taskInfo: RunningTaskInfo)
+
+ /**
+ * Whether the caption items should use the 'light' color variant so that there's good contrast
+ * with the caption background color.
+ */
+ protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean {
+ return Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5
+ }
+}
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml
new file mode 100644
index 000000000000..aafe46681336
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_device_other.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/system_accent1_200">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M7,20H4Q3.175,20 2.588,19.413Q2,18.825 2,18V6Q2,5.175 2.588,4.588Q3.175,4 4,4H20V6H4Q4,6 4,6Q4,6 4,6V18Q4,18 4,18Q4,18 4,18H7ZM9,20V18.2Q8.55,17.775 8.275,17.225Q8,16.675 8,16Q8,15.325 8.275,14.775Q8.55,14.225 9,13.8V12H13V13.8Q13.45,14.225 13.725,14.775Q14,15.325 14,16Q14,16.675 13.725,17.225Q13.45,17.775 13,18.2V20ZM11,17.5Q11.65,17.5 12.075,17.075Q12.5,16.65 12.5,16Q12.5,15.35 12.075,14.925Q11.65,14.5 11,14.5Q10.35,14.5 9.925,14.925Q9.5,15.35 9.5,16Q9.5,16.65 9.925,17.075Q10.35,17.5 11,17.5ZM21,20H16Q15.575,20 15.288,19.712Q15,19.425 15,19V10Q15,9.575 15.288,9.287Q15.575,9 16,9H21Q21.425,9 21.712,9.287Q22,9.575 22,10V19Q22,19.425 21.712,19.712Q21.425,20 21,20ZM17,18H20V11H17Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml
index 97d201d625c8..190e0a8020a6 100644
--- a/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_glasses.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="@android:color/system_neutral1_200">
+ android:tint="@android:color/system_accent1_200">
<path
android:fillColor="@android:color/white"
android:pathData="M6.85,15Q7.625,15 8.238,14.55Q8.85,14.1 9.1,13.375L9.475,12.225Q9.875,11.025 9.275,10.012Q8.675,9 7.55,9H4.025L4.5,12.925Q4.625,13.8 5.287,14.4Q5.95,15 6.85,15ZM17.15,15Q18.05,15 18.712,14.4Q19.375,13.8 19.5,12.925L19.975,9H16.475Q15.35,9 14.75,10.025Q14.15,11.05 14.55,12.25L14.9,13.375Q15.15,14.1 15.762,14.55Q16.375,15 17.15,15ZM6.85,17Q5.2,17 3.963,15.912Q2.725,14.825 2.525,13.175L2,9H1V7H7.55Q8.65,7 9.562,7.537Q10.475,8.075 11,9H13.025Q13.55,8.075 14.463,7.537Q15.375,7 16.475,7H23V9H22L21.475,13.175Q21.275,14.825 20.038,15.912Q18.8,17 17.15,17Q15.725,17 14.588,16.188Q13.45,15.375 13,14.025L12.625,12.9Q12.575,12.725 12.525,12.537Q12.475,12.35 12.425,12H11.575Q11.525,12.3 11.475,12.487Q11.425,12.675 11.375,12.85L11,14Q10.55,15.35 9.413,16.175Q8.275,17 6.85,17Z"/>
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml
index 161186155340..78120a3db999 100644
--- a/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_permission_nearby_devices.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_200">
<path android:fillColor="@android:color/system_accent1_200"
android:pathData="M12,16.4 L7.6,12 12,7.6 16.4,12ZM13.4,21.375Q13.125,21.65 12.75,21.8Q12.375,21.95 12,21.95Q11.625,21.95 11.25,21.8Q10.875,21.65 10.6,21.375L2.625,13.4Q2.35,13.125 2.2,12.75Q2.05,12.375 2.05,12Q2.05,11.625 2.2,11.25Q2.35,10.875 2.625,10.6L10.575,2.65Q10.875,2.35 11.238,2.2Q11.6,2.05 12,2.05Q12.4,2.05 12.762,2.2Q13.125,2.35 13.425,2.65L21.375,10.6Q21.65,10.875 21.8,11.25Q21.95,11.625 21.95,12Q21.95,12.375 21.8,12.75Q21.65,13.125 21.375,13.4ZM12,19.2 L19.2,12Q19.2,12 19.2,12Q19.2,12 19.2,12L12,4.8Q12,4.8 12,4.8Q12,4.8 12,4.8L4.8,12Q4.8,12 4.8,12Q4.8,12 4.8,12L12,19.2Q12,19.2 12,19.2Q12,19.2 12,19.2Z"/>
</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml
new file mode 100644
index 000000000000..f1fda1772874
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_watch.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/system_accent1_200">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M9,22 L7.65,17.45Q6.45,16.5 5.725,15.075Q5,13.65 5,12Q5,10.35 5.725,8.925Q6.45,7.5 7.65,6.55L9,2H15L16.35,6.55Q17.55,7.5 18.275,8.925Q19,10.35 19,12Q19,13.65 18.275,15.075Q17.55,16.5 16.35,17.45L15,22ZM12,17Q14.075,17 15.538,15.537Q17,14.075 17,12Q17,9.925 15.538,8.462Q14.075,7 12,7Q9.925,7 8.463,8.462Q7,9.925 7,12Q7,14.075 8.463,15.537Q9.925,17 12,17ZM10.1,5.25Q11.075,4.975 12,4.975Q12.925,4.975 13.9,5.25L13.5,4H10.5ZM10.5,20H13.5L13.9,18.75Q12.925,19.025 12,19.025Q11.075,19.025 10.1,18.75ZM10.1,4H10.5H13.5H13.9Q12.925,4 12,4Q11.075,4 10.1,4ZM10.5,20H10.1Q11.075,20 12,20Q12.925,20 13.9,20H13.5Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml b/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml
index 15f6987bb551..dc12d12b49ad 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml
@@ -19,7 +19,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_600">
<path android:fillColor="@android:color/white"
android:pathData="M7,20H4Q3.175,20 2.588,19.413Q2,18.825 2,18V6Q2,5.175 2.588,4.588Q3.175,4 4,4H20V6H4Q4,6 4,6Q4,6 4,6V18Q4,18 4,18Q4,18 4,18H7ZM9,20V18.2Q8.55,17.775 8.275,17.225Q8,16.675 8,16Q8,15.325 8.275,14.775Q8.55,14.225 9,13.8V12H13V13.8Q13.45,14.225 13.725,14.775Q14,15.325 14,16Q14,16.675 13.725,17.225Q13.45,17.775 13,18.2V20ZM11,17.5Q11.65,17.5 12.075,17.075Q12.5,16.65 12.5,16Q12.5,15.35 12.075,14.925Q11.65,14.5 11,14.5Q10.35,14.5 9.925,14.925Q9.5,15.35 9.5,16Q9.5,16.65 9.925,17.075Q10.35,17.5 11,17.5ZM21,20H16Q15.575,20 15.288,19.712Q15,19.425 15,19V10Q15,9.575 15.288,9.287Q15.575,9 16,9H21Q21.425,9 21.712,9.287Q22,9.575 22,10V19Q22,19.425 21.712,19.712Q21.425,20 21,20ZM17,18H20V11H17Z"/>
</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml b/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml
index 90655203fe93..0baf7ef7917f 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_glasses.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_600">
<path
android:fillColor="@android:color/white"
android:pathData="M6.85,15Q7.625,15 8.238,14.55Q8.85,14.1 9.1,13.375L9.475,12.225Q9.875,11.025 9.275,10.012Q8.675,9 7.55,9H4.025L4.5,12.925Q4.625,13.8 5.287,14.4Q5.95,15 6.85,15ZM17.15,15Q18.05,15 18.712,14.4Q19.375,13.8 19.5,12.925L19.975,9H16.475Q15.35,9 14.75,10.025Q14.15,11.05 14.55,12.25L14.9,13.375Q15.15,14.1 15.762,14.55Q16.375,15 17.15,15ZM6.85,17Q5.2,17 3.963,15.912Q2.725,14.825 2.525,13.175L2,9H1V7H7.55Q8.65,7 9.562,7.537Q10.475,8.075 11,9H13.025Q13.55,8.075 14.463,7.537Q15.375,7 16.475,7H23V9H22L21.475,13.175Q21.275,14.825 20.038,15.912Q18.8,17 17.15,17Q15.725,17 14.588,16.188Q13.45,15.375 13,14.025L12.625,12.9Q12.575,12.725 12.525,12.537Q12.475,12.35 12.425,12H11.575Q11.525,12.3 11.475,12.487Q11.425,12.675 11.375,12.85L11,14Q10.55,15.35 9.413,16.175Q8.275,17 6.85,17Z"/>
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_watch.xml b/packages/CompanionDeviceManager/res/drawable/ic_watch.xml
index d7a28d949997..0f6b21f6767c 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_watch.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_watch.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="@android:color/system_accent1_600">
<path android:fillColor="@android:color/white"
android:pathData="M9,22 L7.65,17.45Q6.45,16.5 5.725,15.075Q5,13.65 5,12Q5,10.35 5.725,8.925Q6.45,7.5 7.65,6.55L9,2H15L16.35,6.55Q17.55,7.5 18.275,8.925Q19,10.35 19,12Q19,13.65 18.275,15.075Q17.55,16.5 16.35,17.45L15,22ZM12,17Q14.075,17 15.538,15.537Q17,14.075 17,12Q17,9.925 15.538,8.462Q14.075,7 12,7Q9.925,7 8.463,8.462Q7,9.925 7,12Q7,14.075 8.463,15.537Q9.925,17 12,17ZM10.1,5.25Q11.075,4.975 12,4.975Q12.925,4.975 13.9,5.25L13.5,4H10.5ZM10.5,20H13.5L13.9,18.75Q12.925,19.025 12,19.025Q11.075,19.025 10.1,18.75ZM10.1,4H10.5H13.5H13.9Q12.925,4 12,4Q11.075,4 10.1,4ZM10.5,20H10.1Q11.075,20 12,20Q12.925,20 13.9,20H13.5Z"/>
</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index d1d2c70134c6..d470d4c7b3f6 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -38,8 +38,7 @@
android:layout_width="match_parent"
android:layout_height="32dp"
android:gravity="center"
- android:layout_marginTop="18dp"
- android:tint="@android:color/system_accent1_600"/>
+ android:layout_marginTop="18dp" />
<LinearLayout style="@style/Description">
<TextView
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index ac5294ad373a..79f04b92aecc 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -29,7 +29,6 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="24dp"
- android:tint="@android:color/system_accent1_600"
android:importantForAccessibility="no"
android:contentDescription="@null"/>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 99b776cb939f..71ae578ec310 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -39,6 +39,7 @@ import static com.android.companiondevicemanager.CompanionDeviceResources.TITLES
import static com.android.companiondevicemanager.Utils.getApplicationLabel;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getIcon;
+import static com.android.companiondevicemanager.Utils.getImageColor;
import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon;
import static com.android.companiondevicemanager.Utils.getVendorHeaderName;
import static com.android.companiondevicemanager.Utils.hasVendorIcon;
@@ -56,7 +57,6 @@ import android.companion.CompanionDeviceManager;
import android.companion.IAssociationRequestCallback;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Color;
@@ -463,8 +463,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements
final Drawable vendorIcon;
final CharSequence vendorName;
final Spanned title;
- int nightModeFlags = getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK;
if (!SUPPORTED_SELF_MANAGED_PROFILES.contains(deviceProfile)) {
throw new RuntimeException("Unsupported profile " + deviceProfile);
@@ -477,8 +475,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
vendorName = getVendorHeaderName(this, packageName, userId);
mVendorHeaderImage.setImageDrawable(vendorIcon);
if (hasVendorIcon(this, packageName, userId)) {
- int color = nightModeFlags == Configuration.UI_MODE_NIGHT_YES
- ? android.R.color.system_accent1_200 : android.R.color.system_accent1_600;
+ int color = getImageColor(this);
mVendorHeaderImage.setColorFilter(getResources().getColor(color, /* Theme= */null));
}
} catch (PackageManager.NameNotFoundException e) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index 328c67ebb216..d8348d1c9218 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static com.android.companiondevicemanager.Utils.getIcon;
+import static com.android.companiondevicemanager.Utils.getImageColor;
import android.content.Context;
import android.view.LayoutInflater;
@@ -65,6 +66,10 @@ class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolde
viewHolder.mImageView.setImageDrawable(
getIcon(mContext, android.R.drawable.stat_sys_data_bluetooth));
}
+
+ viewHolder.mImageView.setColorFilter(
+ mContext.getResources().getColor(getImageColor(mContext), /* Theme= */null));
+
return viewHolder;
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index fceca91ce617..8c14f8037e64 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -120,6 +121,20 @@ class Utils {
}
}
+ private static boolean isDarkTheme(@NonNull Context context) {
+ int nightModeFlags = context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ return nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ // Get image color for the corresponding theme.
+ static int getImageColor(@NonNull Context context) {
+ if (isDarkTheme(context)) {
+ return android.R.color.system_accent1_200;
+ } else {
+ return android.R.color.system_accent1_600;
+ }
+ }
/**
* Getting ApplicationInfo from meta-data.
*/
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8b38debef942..941697b2392f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -376,7 +376,7 @@ android_app {
name: "SystemUIRobo-stub",
defaults: [
"platform_app_defaults",
- "SystemUI_app_defaults",
+ "SystemUI_optimized_defaults",
"SystemUI_compose_defaults",
],
manifest: "tests/AndroidManifest-base.xml",
@@ -443,7 +443,7 @@ soong_config_module_type {
}
systemui_optimized_java_defaults {
- name: "SystemUI_app_defaults",
+ name: "SystemUI_optimized_defaults",
soong_config_variables: {
SYSTEMUI_OPTIMIZE_JAVA: {
optimize: {
@@ -452,12 +452,10 @@ systemui_optimized_java_defaults {
shrink: true,
shrink_resources: true,
proguard_compatibility: false,
- proguard_flags_files: ["proguard.flags"],
},
conditions_default: {
optimize: {
proguard_compatibility: false,
- proguard_flags_files: ["proguard.flags"],
},
},
},
@@ -468,7 +466,7 @@ android_app {
name: "SystemUI",
defaults: [
"platform_app_defaults",
- "SystemUI_app_defaults",
+ "SystemUI_optimized_defaults",
],
static_libs: [
"SystemUI-core",
@@ -483,6 +481,9 @@ android_app {
kotlincflags: ["-Xjvm-default=enable"],
dxflags: ["--multi-dex"],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
required: [
"privapp_whitelist_com.android.systemui",
"wmshell.protolog.json.gz",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f2f0c597b86c..085f202861bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -480,6 +480,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
sCurrentUser = currentUser;
}
+ /**
+ * @deprecated This can potentially return unexpected values in a multi user scenario
+ * as this state is managed by another component. Consider using {@link UserTracker}.
+ */
+ @Deprecated
public synchronized static int getCurrentUser() {
return sCurrentUser;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 779be2b102a6..fda227795915 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -28,7 +28,6 @@ import static android.view.View.VISIBLE;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
-import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
@@ -99,6 +98,7 @@ import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteracto
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.log.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -146,6 +146,7 @@ public class KeyguardIndicationController {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AuthController mAuthController;
private final KeyguardLogger mKeyguardLogger;
+ private final UserTracker mUserTracker;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -251,7 +252,8 @@ public class KeyguardIndicationController {
FaceHelpMessageDeferral faceHelpMessageDeferral,
KeyguardLogger keyguardLogger,
AlternateBouncerInteractor alternateBouncerInteractor,
- AlarmManager alarmManager
+ AlarmManager alarmManager,
+ UserTracker userTracker
) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
@@ -275,6 +277,7 @@ public class KeyguardIndicationController {
mKeyguardLogger = keyguardLogger;
mScreenLifecycle.addObserver(mScreenObserver);
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mUserTracker = userTracker;
mFaceAcquiredMessageDeferral = faceHelpMessageDeferral;
mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
@@ -475,6 +478,10 @@ public class KeyguardIndicationController {
}
}
+ private int getCurrentUser() {
+ return mUserTracker.getUserId();
+ }
+
private void updateLockScreenOwnerInfo() {
// Check device owner info on a bg thread.
// It makes multiple IPCs that could block the thread it's run on.
@@ -1166,8 +1173,7 @@ public class KeyguardIndicationController {
mContext.getString(R.string.keyguard_unlock)
);
} else if (fpAuthFailed
- && mKeyguardUpdateMonitor.getUserHasTrust(
- KeyguardUpdateMonitor.getCurrentUser())) {
+ && mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())) {
showBiometricMessage(
getTrustGrantedIndication(),
mContext.getString(R.string.keyguard_unlock)
@@ -1421,7 +1427,7 @@ public class KeyguardIndicationController {
private boolean canUnlockWithFingerprint() {
return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser());
+ getCurrentUser());
}
private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index f866d65e2d2d..d0c6215a55d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -105,10 +105,14 @@ class MobileConnectionRepositoryImpl(
* The reason we need to do this is because TelephonyManager limits the number of registered
* listeners per-process, so we don't want to create a new listener for every callback.
*
- * A note on the design for back pressure here: We use the [coalesce] operator here to change
- * the backpressure strategy to store exactly the last callback event of _each type_ here, as
- * opposed to the default strategy which is to drop the oldest event (regardless of type). This
- * means that we should never miss any single event as long as the flow has been started.
+ * A note on the design for back pressure here: We don't control _which_ telephony callback
+ * comes in first, since we register every relevant bit of information as a batch. E.g., if a
+ * downstream starts collecting on a field which is backed by
+ * [TelephonyCallback.ServiceStateListener], it's not possible for us to guarantee that _that_
+ * callback comes in -- the first callback could very well be
+ * [TelephonyCallback.DataActivityListener], which would promptly be dropped if we didn't keep
+ * it tracked. We use the [scan] operator here to track the most recent callback of _each type_
+ * here. See [TelephonyCallbackState] to see how the callbacks are stored.
*/
private val callbackEvents: StateFlow<TelephonyCallbackState> = run {
val initial = TelephonyCallbackState()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 251acedcc66e..569f90b64609 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -105,6 +105,7 @@ import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -188,6 +189,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private AuthController mAuthController;
@Mock
private AlarmManager mAlarmManager;
+ @Mock
+ private UserTracker mUserTracker;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -209,6 +212,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private BroadcastReceiver mBroadcastReceiver;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private TestableLooper mTestableLooper;
+ private final int mCurrentUserId = 1;
private KeyguardIndicationTextView mTextView; // AOD text
@@ -260,6 +264,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
.thenReturn(mDisclosureGeneric);
when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString()))
.thenReturn(mDisclosureWithOrganization);
+ when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
mWakeLock = new WakeLockFake();
mWakeLockBuilder = new WakeLockFake.Builder(mContext);
@@ -291,7 +296,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mKeyguardBypassController, mAccessibilityManager,
mFaceHelpMessageDeferral, mock(KeyguardLogger.class),
mAlternateBouncerInteractor,
- mAlarmManager
+ mAlarmManager,
+ mUserTracker
);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -813,7 +819,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void faceErrorTimeout_whenFingerprintEnrolled_doesNotShowMessage() {
createController();
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- 0)).thenReturn(true);
+ getCurrentUser())).thenReturn(true);
String message = "A message";
mController.setVisible(true);
@@ -828,7 +834,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN fingerprint enrolled
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- 0)).thenReturn(true);
+ getCurrentUser())).thenReturn(true);
// WHEN help messages received that are allowed to show
final String helpString = "helpString";
@@ -855,7 +861,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN fingerprint enrolled
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- 0)).thenReturn(true);
+ getCurrentUser())).thenReturn(true);
// WHEN help messages received that aren't supposed to show
final String helpString = "helpString";
@@ -882,7 +888,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN fingerprint NOT enrolled
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- 0)).thenReturn(false);
+ getCurrentUser())).thenReturn(false);
// WHEN help messages received
final Set<CharSequence> helpStrings = new HashSet<>();
@@ -913,7 +919,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN fingerprint NOT enrolled
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- 0)).thenReturn(false);
+ getCurrentUser())).thenReturn(false);
// WHEN help message received and deferred message is valid
final String helpString = "helpMsg";
@@ -944,7 +950,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN fingerprint enrolled
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- 0)).thenReturn(true);
+ getCurrentUser())).thenReturn(true);
// WHEN help message received and deferredMessage is valid
final String helpString = "helpMsg";
@@ -1173,7 +1179,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// WHEN trust is granted
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onTrustChanged(KeyguardUpdateMonitor.getCurrentUser());
+ mKeyguardUpdateMonitorCallback.onTrustChanged(getCurrentUser());
// THEN verify the trust granted message shows
verifyIndicationMessage(
@@ -1238,7 +1244,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void coEx_faceSuccess_showsPressToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(false);
@@ -1262,7 +1268,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void coEx_faceSuccess_touchExplorationEnabled_showsFaceUnlockedSwipeToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(true);
@@ -1286,7 +1292,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void coEx_faceSuccess_a11yEnabled_showsFaceUnlockedSwipeToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(true);
@@ -1309,7 +1315,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void faceOnly_faceSuccess_showsFaceUnlockedSwipeToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, no udfps supported
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
createController();
@@ -1331,7 +1337,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void udfpsOnly_a11yEnabled_showsSwipeToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(true);
@@ -1351,7 +1357,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void udfpsOnly_showsPressToOpen() {
// GIVEN bouncer isn't showing, udfps is supported, a11y is NOT enabled, can skip bouncer
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(false);
@@ -1372,7 +1378,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN bouncer isn't showing, can skip bouncer, no security (udfps isn't supported,
// face wasn't authenticated)
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
createController();
@@ -1390,7 +1396,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void cannotSkipBouncer_showSwipeToUnlockHint() {
// GIVEN bouncer isn't showing and cannot skip bouncer
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
.thenReturn(false);
createController();
mController.setVisible(true);
@@ -1746,10 +1752,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private void setupFingerprintUnlockPossible(boolean possible) {
when(mKeyguardUpdateMonitor
- .getCachedIsUnlockWithFingerprintPossible(KeyguardUpdateMonitor.getCurrentUser()))
+ .getCachedIsUnlockWithFingerprintPossible(getCurrentUser()))
.thenReturn(possible);
}
+ private int getCurrentUser() {
+ return mCurrentUserId;
+ }
+
private void onFaceLockoutError(String errMsg) {
mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_LOCKOUT_PERMANENT,
errMsg,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 542b688b162d..934e1c64c6da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -25,7 +25,6 @@ import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.DataActivityListener
import android.telephony.TelephonyCallback.ServiceStateListener
-import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
@@ -68,6 +67,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameMo
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
@@ -392,13 +392,17 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
- val type = NETWORK_TYPE_UNKNOWN
- val expected = UnknownNetworkType
- val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
+ val ti =
+ telephonyDisplayInfo(
+ networkType = NETWORK_TYPE_UNKNOWN,
+ overrideNetworkType = NETWORK_TYPE_UNKNOWN,
+ )
+
callback.onDisplayInfoChanged(ti)
+ val expected = UnknownNetworkType
assertThat(latest).isEqualTo(expected)
- assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(type))
+ assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(NETWORK_TYPE_UNKNOWN))
job.cancel()
}
@@ -412,14 +416,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val overrideType = OVERRIDE_NETWORK_TYPE_NONE
val type = NETWORK_TYPE_LTE
- val expected = DefaultNetworkType(mobileMappings.toIconKey(type))
- val ti =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.overrideNetworkType).thenReturn(overrideType)
- whenever(it.networkType).thenReturn(type)
- }
+ val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = overrideType)
callback.onDisplayInfoChanged(ti)
+ val expected = DefaultNetworkType(mobileMappings.toIconKey(type))
assertThat(latest).isEqualTo(expected)
job.cancel()
@@ -433,14 +433,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val type = OVERRIDE_NETWORK_TYPE_LTE_CA
- val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
- val ti =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.networkType).thenReturn(type)
- whenever(it.overrideNetworkType).thenReturn(type)
- }
+ val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = type)
callback.onDisplayInfoChanged(ti)
+ val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
assertThat(latest).isEqualTo(expected)
job.cancel()
@@ -455,14 +451,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
val unknown = NETWORK_TYPE_UNKNOWN
val type = OVERRIDE_NETWORK_TYPE_LTE_CA
- val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
- val ti =
- mock<TelephonyDisplayInfo>().also {
- whenever(it.networkType).thenReturn(unknown)
- whenever(it.overrideNetworkType).thenReturn(type)
- }
+ val ti = telephonyDisplayInfo(unknown, type)
callback.onDisplayInfoChanged(ti)
+ val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
assertThat(latest).isEqualTo(expected)
job.cancel()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index bbf04ed28fd7..9da9ff72d380 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -64,10 +64,14 @@ import org.mockito.MockitoAnnotations
*
* Kind of like an interaction test case build just for [TelephonyCallback]
*
- * The list of telephony callbacks we use is: [TelephonyCallback.CarrierNetworkListener]
- * [TelephonyCallback.DataActivityListener] [TelephonyCallback.DataConnectionStateListener]
- * [TelephonyCallback.DataEnabledListener] [TelephonyCallback.DisplayInfoListener]
- * [TelephonyCallback.ServiceStateListener] [TelephonyCallback.SignalStrengthsListener]
+ * The list of telephony callbacks we use is:
+ * - [TelephonyCallback.CarrierNetworkListener]
+ * - [TelephonyCallback.DataActivityListener]
+ * - [TelephonyCallback.DataConnectionStateListener]
+ * - [TelephonyCallback.DataEnabledListener]
+ * - [TelephonyCallback.DisplayInfoListener]
+ * - [TelephonyCallback.ServiceStateListener]
+ * - [TelephonyCallback.SignalStrengthsListener]
*
* Because each of these callbacks comes in on the same callbackFlow, collecting on a field backed
* by only a single callback can immediately create backpressure on the other fields related to a
@@ -201,7 +205,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
200 /* unused */
)
- // Send a bunch of events that we don't care about, to overrun the replay buffer
flipActivity(100, activityCallback)
val connectionJob = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
@@ -225,7 +228,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
enabledCallback.onDataEnabledChanged(true, 1 /* unused */)
- // Send a bunch of events that we don't care about, to overrun the replay buffer
flipActivity(100, activityCallback)
val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
@@ -252,7 +254,6 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
val ti = mock<TelephonyDisplayInfo>().also { whenever(it.networkType).thenReturn(type) }
displayInfoCallback.onDisplayInfoChanged(ti)
- // Send a bunch of events that we don't care about, to overrun the replay buffer
flipActivity(100, activityCallback)
val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
index d07b96f6609e..cf815c27a0bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.telephony.CellSignalStrengthCdma
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
+import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -48,6 +49,12 @@ object MobileTelephonyHelpers {
return signalStrength
}
+ fun telephonyDisplayInfo(networkType: Int, overrideNetworkType: Int) =
+ mock<TelephonyDisplayInfo>().also {
+ whenever(it.networkType).thenReturn(networkType)
+ whenever(it.overrideNetworkType).thenReturn(overrideNetworkType)
+ }
+
inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T {
val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>()
assertThat(cbs.size).isEqualTo(1)
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 742c94af3211..d6f1348a6f3d 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -343,10 +343,8 @@ public class MmsServiceBroker extends SystemService {
// Check if user is associated with the subscription
if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
Binder.getCallingUserHandle())) {
- if (TelephonyUtils.isUidForeground(mContext, Binder.getCallingUid())) {
- TelephonyUtils.showErrorIfSubscriptionAssociatedWithManagedProfile(mContext,
- subId);
- }
+ TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext,
+ subId, Binder.getCallingUid(), callingPkg);
return;
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 4d469639be6f..33d4004a9027 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -242,7 +242,7 @@ public class BroadcastConstants {
*/
public boolean CORE_DEFER_UNTIL_ACTIVE = DEFAULT_CORE_DEFER_UNTIL_ACTIVE;
private static final String KEY_CORE_DEFER_UNTIL_ACTIVE = "bcast_core_defer_until_active";
- private static final boolean DEFAULT_CORE_DEFER_UNTIL_ACTIVE = true;
+ private static final boolean DEFAULT_CORE_DEFER_UNTIL_ACTIVE = false;
// Settings override tracking for this instance
private String mSettingsKey;
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 18eebe45a759..b4b8cb2a370d 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -619,10 +619,10 @@ public final class SuspendPackageHelper {
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
- extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
- null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
- null /* broadcastAllowList */,
+ extras, flags, null /* targetPkg */, null /* finishedReceiver */,
+ new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
mPm.snapshotComputer(), callingUid, intentExtras),
null /* bOptions */));
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 741464072fd8..b066cad29e9a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1275,7 +1275,7 @@ public class UserManagerService extends IUserManager.Stub {
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
getDevicePolicyManagerInternal().broadcastIntentToManifestReceivers(
intent, parentHandle, /* requiresPermission= */ true);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, parentHandle);
}
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index c0d71ac26853..579d4e3562b4 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -26,10 +26,10 @@
]
},
{
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
},
{
"include-filter": "android.permission.cts.PermissionMaxSdkVersionTest"
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 094e70fe8f80..9f1cb1a451c6 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -29,16 +29,16 @@
]
},
{
- "name": "CtsPermission2TestCases",
+ "name": "CtsPermissionPolicyTestCases",
"options": [
{
- "include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
},
{
- "include-filter": "android.permission2.cts.RestrictedStoragePermissionSharedUidTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedStoragePermissionSharedUidTest"
},
{
- "include-filter": "android.permission2.cts.RestrictedStoragePermissionTest"
+ "include-filter": "android.permissionpolicy.cts.RestrictedStoragePermissionTest"
}
]
},
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 5e066faf0e90..f8fb76acf81e 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -50,8 +50,6 @@ import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationAdapter;
-import android.view.WindowManager;
-import android.window.RemoteTransition;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -562,9 +560,8 @@ public class ActivityStartController {
final Task rootTask = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
.getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
if (rootTask == null) return false;
- final RemoteTransition remote = options.getRemoteTransition();
final ActivityRecord r = rootTask.topRunningActivity();
- if (r == null || r.isVisibleRequested() || !r.attachedToProcess() || remote == null
+ if (r == null || r.isVisibleRequested() || !r.attachedToProcess()
|| !r.mActivityComponent.equals(intent.getComponent())
// Recents keeps invisible while device is locked.
|| r.mDisplayContent.isKeyguardLocked()) {
@@ -573,47 +570,13 @@ public class ActivityStartController {
mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, r);
final ActivityMetricsLogger.LaunchingState launchingState =
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
- final Transition transition = new Transition(WindowManager.TRANSIT_TO_FRONT,
- 0 /* flags */, r.mTransitionController, mService.mWindowManager.mSyncEngine);
- if (r.mTransitionController.isCollecting()) {
- // Special case: we are entering recents while an existing transition is running. In
- // this case, we know it's safe to "defer" the activity launch, so lets do so now so
- // that it can get its own transition and thus update launcher correctly.
- mService.mWindowManager.mSyncEngine.queueSyncSet(
- () -> {
- if (r.isAttached()) {
- r.mTransitionController.moveToCollecting(transition);
- }
- },
- () -> {
- if (r.isAttached() && transition.isCollecting()) {
- startExistingRecentsIfPossibleInner(options, r, rootTask,
- launchingState, remote, transition);
- }
- });
- } else {
- r.mTransitionController.moveToCollecting(transition);
- startExistingRecentsIfPossibleInner(options, r, rootTask, launchingState, remote,
- transition);
- }
- return true;
- }
-
- private void startExistingRecentsIfPossibleInner(ActivityOptions options, ActivityRecord r,
- Task rootTask, ActivityMetricsLogger.LaunchingState launchingState,
- RemoteTransition remoteTransition, Transition transition) {
final Task task = r.getTask();
mService.deferWindowLayout();
try {
final TransitionController controller = r.mTransitionController;
if (controller.getTransitionPlayer() != null) {
- controller.requestStartTransition(transition, task, remoteTransition,
- null /* displayChange */);
controller.collect(task);
controller.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
- } else {
- // The transition player might be died when executing the queued transition.
- transition.abort();
}
task.moveToFront("startExistingRecents");
task.mInResumeTopActivity = true;
@@ -624,6 +587,7 @@ public class ActivityStartController {
task.mInResumeTopActivity = false;
mService.continueWindowLayout();
}
+ return true;
}
void registerRemoteAnimationForNextActivityStart(String packageName,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ce29564d0b02..12be1d3186a1 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1582,19 +1582,19 @@ class ActivityStarter {
}
}
if (isTransientLaunch) {
- if (forceTransientTransition && newTransition != null) {
- newTransition.collect(mLastStartActivityRecord);
- newTransition.collect(mPriorAboveTask);
+ if (forceTransientTransition) {
+ transitionController.collect(mLastStartActivityRecord);
+ transitionController.collect(mPriorAboveTask);
}
// `started` isn't guaranteed to be the actual relevant activity, so we must wait
// until after we launched to identify the relevant activity.
transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
- if (forceTransientTransition && newTransition != null) {
+ if (forceTransientTransition) {
final DisplayContent dc = mLastStartActivityRecord.getDisplayContent();
// update wallpaper target to TransientHide
dc.mWallpaperController.adjustWallpaperWindows();
// execute transition because there is no change
- newTransition.setReady(dc, true /* ready */);
+ transitionController.setReady(dc, true /* ready */);
}
}
if (!userLeaving) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 992743ab8593..555cd38806e6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1240,25 +1240,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
- // A quick path (skip general intent/task resolving) to start recents animation if the
- // recents (or home) activity is available in background.
- if (opts != null && opts.getOriginalOptions().getTransientLaunch()
- && isCallerRecents(Binder.getCallingUid())) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
- if (mActivityStartController.startExistingRecentsIfPossible(
- intent, opts.getOriginalOptions())) {
- return ActivityManager.START_TASK_TO_FRONT;
- }
- // Else follow the standard launch procedure.
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- Binder.restoreCallingIdentity(origId);
- }
- }
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
@@ -5718,6 +5699,23 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
BackgroundStartPrivileges backgroundStartPrivileges) {
assertPackageMatchesCallingUid(callingPackage);
+ // A quick path (skip general intent/task resolving) to start recents animation if the
+ // recents (or home) activity is available in background.
+ if (options != null && options.getOriginalOptions() != null
+ && options.getOriginalOptions().getTransientLaunch() && isCallerRecents(uid)) {
+ try {
+ synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
+ if (mActivityStartController.startExistingRecentsIfPossible(
+ intent, options.getOriginalOptions())) {
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ // Else follow the standard launch procedure.
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 4e058a8e05e2..8082cdbf6ec8 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -27,6 +27,7 @@ import android.credentials.GetCredentialResponse;
import android.credentials.IGetCredentialCallback;
import android.credentials.ui.ProviderData;
import android.credentials.ui.RequestInfo;
+import android.os.Binder;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
@@ -84,11 +85,12 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
mChosenProviderFinalPhaseMetric.setUiCallStartTimeNanoseconds(System.nanoTime());
try {
- mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
+ Binder.withCleanCallingIdentity(() ->
+ mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
- mRequestId, mClientRequest, mClientAppInfo.getPackageName()),
- providerDataList));
- } catch (RemoteException e) {
+ mRequestId, mClientRequest, mClientAppInfo.getPackageName()),
+ providerDataList)));
+ } catch (RuntimeException e) {
mChosenProviderFinalPhaseMetric.setUiReturned(false);
respondToClientWithErrorAndFinish(
GetCredentialException.TYPE_UNKNOWN, "Unable to instantiate selector");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e9c50b54f5dd..269c4abf537e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -225,6 +225,7 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPR
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER;
+import static android.provider.DeviceConfig.NAMESPACE_TELEPHONY;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -426,6 +427,7 @@ import android.security.keystore.AttestationUtils;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -3324,7 +3326,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
onLockSettingsReady();
loadAdminDataAsync();
mOwners.systemReady();
- if (isWorkProfileTelephonyFlagEnabled()) {
+ if (isWorkProfileTelephonyEnabled()) {
applyManagedSubscriptionsPolicyIfRequired();
}
break;
@@ -3534,26 +3536,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
updatePermissionPolicyCache(userId);
updateAdminCanGrantSensorsPermissionCache(userId);
- final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
- boolean isManagedSubscription;
+ final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
synchronized (getLockObject()) {
ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
preferentialNetworkServiceConfigs = owner != null
? owner.mPreferentialNetworkServiceConfigs
: List.of(PreferentialNetworkServiceConfig.DEFAULT);
-
- isManagedSubscription = owner != null && owner.mManagedSubscriptionsPolicy != null
- && owner.mManagedSubscriptionsPolicy.getPolicyType()
- == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS;
}
updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
- if (isManagedSubscription) {
- String defaultDialerPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultDialer);
- String defaultSmsPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultSms);
+ if (isProfileOwnerOfOrganizationOwnedDevice(userId)
+ && getManagedSubscriptionsPolicy().getPolicyType()
+ == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+ String defaultDialerPackageName = getOemDefaultDialerPackage();
+ String defaultSmsPackageName = getOemDefaultSmsPackage();
updateDialerAndSmsManagedShortcutsOverrideCache(defaultDialerPackageName,
defaultSmsPackageName);
}
@@ -7640,7 +7637,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
- if (isWorkProfileTelephonyFlagEnabled()) {
+ if (isWorkProfileTelephonyEnabled()) {
clearManagedSubscriptionsPolicy();
clearLauncherShortcutOverrides();
updateTelephonyCrossProfileIntentFilters(parentId, UserHandle.USER_NULL, false);
@@ -10991,8 +10988,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (mSubscriptionsChangedListenerLock) {
pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
}
- pw.println(
- "Flag enable_work_profile_telephony : " + isWorkProfileTelephonyFlagEnabled());
+ pw.println("DPM Flag enable_work_profile_telephony : "
+ + isWorkProfileTelephonyDevicePolicyManagerFlagEnabled());
+ pw.println("Telephony Flag enable_work_profile_telephony : "
+ + isWorkProfileTelephonySubscriptionManagerFlagEnabled());
mHandler.post(() -> handleDump(pw));
dumpResources(pw);
@@ -22705,11 +22704,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
}
- private static boolean isWorkProfileTelephonyFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
- DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+ private boolean isWorkProfileTelephonyEnabled() {
+ return isWorkProfileTelephonyDevicePolicyManagerFlagEnabled()
+ && isWorkProfileTelephonySubscriptionManagerFlagEnabled();
+ }
+
+ private boolean isWorkProfileTelephonyDevicePolicyManagerFlagEnabled() {
+ return DeviceConfig.getBoolean(NAMESPACE_DEVICE_POLICY_MANAGER,
+ ENABLE_WORK_PROFILE_TELEPHONY_FLAG, DEFAULT_WORK_PROFILE_TELEPHONY_FLAG);
+ }
+
+ private boolean isWorkProfileTelephonySubscriptionManagerFlagEnabled() {
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ return DeviceConfig.getBoolean(NAMESPACE_TELEPHONY, ENABLE_WORK_PROFILE_TELEPHONY_FLAG,
+ false);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
@Override
@@ -22822,7 +22834,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
- if (isWorkProfileTelephonyFlagEnabled()) {
+ if (isWorkProfileTelephonyEnabled()) {
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
@@ -22836,7 +22848,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
- if (!isWorkProfileTelephonyFlagEnabled()) {
+ if (!isWorkProfileTelephonyEnabled()) {
throw new UnsupportedOperationException("This api is not enabled");
}
CallerIdentity caller = getCallerIdentity();
@@ -22844,9 +22856,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
"This policy can only be set by a profile owner on an organization-owned "
+ "device.");
+ int parentUserId = getProfileParentId(caller.getUserId());
synchronized (getLockObject()) {
final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM) && !isAdminTestOnlyLocked(
+ if (hasUserSetupCompleted(parentUserId) && !isAdminTestOnlyLocked(
admin.info.getComponent(), caller.getUserId())) {
throw new IllegalStateException("Not allowed to apply this policy after setup");
}
@@ -22868,7 +22881,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
final long id = mInjector.binderClearCallingIdentity();
try {
- int parentUserId = getProfileParentId(caller.getUserId());
installOemDefaultDialerAndSmsApp(caller.getUserId());
updateTelephonyCrossProfileIntentFilters(parentUserId, caller.getUserId(), true);
} finally {
@@ -22879,10 +22891,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void installOemDefaultDialerAndSmsApp(int targetUserId) {
try {
- String defaultDialerPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultDialer);
- String defaultSmsPackageName = getDefaultRoleHolderPackageName(
- com.android.internal.R.string.config_defaultSms);
+ String defaultDialerPackageName = getOemDefaultDialerPackage();
+ String defaultSmsPackageName = getOemDefaultSmsPackage();
if (defaultDialerPackageName != null) {
mIPackageManager.installExistingPackageAsUser(defaultDialerPackageName,
@@ -22908,6 +22918,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private String getOemDefaultDialerPackage() {
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ return telecomManager.getSystemDialerPackage();
+ }
+
+ private String getOemDefaultSmsPackage() {
+ return mContext.getString(R.string.config_defaultSms);
+ }
+
private void updateDialerAndSmsManagedShortcutsOverrideCache(
String defaultDialerPackageName, String defaultSmsPackageName) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index aaabb286589c..4f74ef8b5f70 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5002,6 +5002,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TELEPHONY,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "true", false);
// Even if the caller is the managed profile, the current user is the user 0
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
@@ -5064,6 +5066,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TELEPHONY,
+ FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false);
}
@Test
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 0325ba648af6..f9b76f4907cf 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -20,6 +20,7 @@ import static android.telephony.Annotation.DataState;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
@@ -255,15 +256,26 @@ public final class TelephonyUtils {
* Show switch to managed profile dialog if subscription is associated with managed profile.
*
* @param context Context object
- * @param subId subscription id
+ * @param subId subscription id
+ * @param callingUid uid for the calling app
+ * @param callingPackage package name of the calling app
*/
- public static void showErrorIfSubscriptionAssociatedWithManagedProfile(Context context,
- int subId) {
+ public static void showSwitchToManagedProfileDialogIfAppropriate(Context context,
+ int subId, int callingUid, String callingPackage) {
if (!isSwitchToManagedProfileDialogFlagEnabled()) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
+ UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+ // We only want to show this dialog, while user actually trying to send the message from
+ // a messaging app, in other cases this dialog don't make sense.
+ if (!TelephonyUtils.isUidForeground(context, callingUid)
+ || !TelephonyUtils.isPackageSMSRoleHolderForUser(context, callingPackage,
+ callingUserHandle)) {
+ return;
+ }
+
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
UserHandle associatedUserHandle = subscriptionManager.getSubscriptionUserHandle(subId);
@@ -295,22 +307,25 @@ public final class TelephonyUtils {
"enable_switch_to_managed_profile_dialog", false);
}
- /**
- * Check if the process with given uid is foreground.
- *
- * @param context context
- * @param uid the caller uid
- * @return true if the process with uid is foreground, false otherwise.
- */
- public static boolean isUidForeground(Context context, int uid) {
- final long token = Binder.clearCallingIdentity();
- try {
- ActivityManager am = context.getSystemService(ActivityManager.class);
- boolean result = am != null && am.getUidImportance(uid)
- == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
- return result;
- } finally {
- Binder.restoreCallingIdentity(token);
+ private static boolean isUidForeground(Context context, int uid) {
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ boolean result = am != null && am.getUidImportance(uid)
+ == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+ return result;
+ }
+
+ private static boolean isPackageSMSRoleHolderForUser(Context context, String callingPackage,
+ UserHandle user) {
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ final List<String> smsRoleHolder = roleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_SMS, user);
+
+ // ROLE_SMS is an exclusive role per user, so there would just be one entry in the
+ // retuned list if not empty
+ if (!smsRoleHolder.isEmpty() && callingPackage.equals(smsRoleHolder.get(0))) {
+ return true;
}
+ return false;
+
}
} \ No newline at end of file