• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.car.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
20 
21 import android.app.ActivityManager;
22 import android.app.ActivityTaskManager;
23 import android.car.Car;
24 import android.car.app.CarActivityManager;
25 import android.car.app.CarSystemUIProxy;
26 import android.car.app.CarTaskViewClient;
27 import android.car.app.CarTaskViewHost;
28 import android.content.Context;
29 import android.content.pm.PackageManager;
30 import android.hardware.display.DisplayManager;
31 import android.os.Binder;
32 import android.os.Process;
33 import android.util.ArraySet;
34 import android.util.Slog;
35 import android.view.Display;
36 
37 import androidx.annotation.NonNull;
38 
39 import com.android.systemui.Dumpable;
40 import com.android.systemui.R;
41 import com.android.systemui.car.CarServiceProvider;
42 import com.android.systemui.car.wm.taskview.RemoteCarTaskViewServerImpl;
43 import com.android.systemui.dump.DumpManager;
44 import com.android.wm.shell.ShellTaskOrganizer;
45 import com.android.wm.shell.common.SyncTransactionQueue;
46 import com.android.wm.shell.dagger.WMSingleton;
47 import com.android.wm.shell.taskview.TaskViewTransitions;
48 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
49 
50 import java.io.PrintWriter;
51 import java.util.List;
52 import java.util.Optional;
53 
54 import javax.inject.Inject;
55 
56 /**
57  * This class provides a concrete implementation for {@link CarSystemUIProxy}. It hosts all the
58  * system ui interaction that is required by other apps.
59  */
60 @WMSingleton
61 public final class CarSystemUIProxyImpl
62         implements CarSystemUIProxy, CarServiceProvider.CarServiceOnConnectedListener, Dumpable {
63     private static final String TAG = CarSystemUIProxyImpl.class.getSimpleName();
64 
65     private final Context mContext;
66     private final SyncTransactionQueue mSyncQueue;
67     private final ShellTaskOrganizer mTaskOrganizer;
68     private final TaskViewTransitions mTaskViewTransitions;
69     private final ArraySet<RemoteCarTaskViewServerImpl> mRemoteCarTaskViewServerSet =
70             new ArraySet<>();
71     private final DisplayManager mDisplayManager;
72     private final Optional<WindowDecorViewModel> mWindowDecorViewModelOptional;
73 
74     private boolean mConnected;
75     private CarActivityManager mCarActivityManager;
76 
77     /**
78      * Returns true if {@link CarSystemUIProxyImpl} should be registered, false otherwise.
79      * This could be false because of reasons like:
80      * <ul>
81      *     <li>Current user is not a system user.</li>
82      *     <li>Or {@code config_registerCarSystemUIProxy} is disabled.</li>
83      * </ul>
84      */
shouldRegisterCarSystemUIProxy(Context context)85     public static boolean shouldRegisterCarSystemUIProxy(Context context) {
86         if (!Process.myUserHandle().isSystem()) {
87             Slog.i(TAG, "Non system user.");
88             return false;
89         }
90         if (!context.getResources().getBoolean(R.bool.config_registerCarSystemUIProxy)) {
91             Slog.i(TAG, "config_registerCarSystemUIProxy disabled");
92             return false;
93         }
94         return true;
95     }
96 
97     @Inject
CarSystemUIProxyImpl( Context context, CarServiceProvider carServiceProvider, SyncTransactionQueue syncTransactionQueue, ShellTaskOrganizer taskOrganizer, TaskViewTransitions taskViewTransitions, DumpManager dumpManager, Optional<WindowDecorViewModel> windowDecorViewModelOptional)98     public CarSystemUIProxyImpl(
99             Context context,
100             CarServiceProvider carServiceProvider,
101             SyncTransactionQueue syncTransactionQueue,
102             ShellTaskOrganizer taskOrganizer,
103             TaskViewTransitions taskViewTransitions,
104             DumpManager dumpManager,
105             Optional<WindowDecorViewModel> windowDecorViewModelOptional) {
106         mContext = context;
107         mTaskOrganizer = taskOrganizer;
108         mSyncQueue = syncTransactionQueue;
109         mTaskViewTransitions = taskViewTransitions;
110         mWindowDecorViewModelOptional = windowDecorViewModelOptional;
111         mDisplayManager = mContext.getSystemService(DisplayManager.class);
112         dumpManager.registerDumpable(this);
113 
114         if (!shouldRegisterCarSystemUIProxy(mContext)) {
115             Slog.i(TAG, "Not registering CarSystemUIProxy.");
116             return;
117         }
118         carServiceProvider.addListener(this);
119     }
120 
121     /** Returns true if a taskview with a launch root task exists on {@code displayId}. */
isLaunchRootTaskPresent(int displayId)122     public boolean isLaunchRootTaskPresent(int displayId) {
123         for (RemoteCarTaskViewServerImpl remoteCarTaskViewServer : mRemoteCarTaskViewServerSet) {
124             if (remoteCarTaskViewServer.hasLaunchRootTaskOnDisplay(displayId)) {
125                 return true;
126             }
127         }
128         return false;
129     }
130 
131     /** Returns the list of all the task views. */
getAllTaskViews()132     public ArraySet<RemoteCarTaskViewServerImpl> getAllTaskViews() {
133         return mRemoteCarTaskViewServerSet;
134     }
135 
136     @Override
createControlledCarTaskView(CarTaskViewClient carTaskViewClient)137     public CarTaskViewHost createControlledCarTaskView(CarTaskViewClient carTaskViewClient) {
138         return createCarTaskView(carTaskViewClient);
139     }
140 
141     @Override
createCarTaskView(CarTaskViewClient carTaskViewClient)142     public CarTaskViewHost createCarTaskView(CarTaskViewClient carTaskViewClient) {
143         ensureManageSystemUIPermission(mContext);
144         RemoteCarTaskViewServerImpl remoteCarTaskViewServerImpl =
145                 new RemoteCarTaskViewServerImpl(
146                         mContext,
147                         mTaskOrganizer,
148                         mSyncQueue,
149                         carTaskViewClient,
150                         this,
151                         mTaskViewTransitions,
152                         mCarActivityManager,
153                         mWindowDecorViewModelOptional);
154         mRemoteCarTaskViewServerSet.add(remoteCarTaskViewServerImpl);
155         return remoteCarTaskViewServerImpl.getHostImpl();
156     }
157 
158     /** Clears the taskview from the internal state. */
onCarTaskViewReleased(RemoteCarTaskViewServerImpl remoteCarTaskViewServer)159     public void onCarTaskViewReleased(RemoteCarTaskViewServerImpl remoteCarTaskViewServer) {
160         mRemoteCarTaskViewServerSet.remove(remoteCarTaskViewServer);
161     }
162 
163     @Override
onConnected(Car car)164     public void onConnected(Car car) {
165         mConnected = true;
166         removeExistingTaskViewTasks();
167 
168         mCarActivityManager = car.getCarManager(CarActivityManager.class);
169         mCarActivityManager.registerCarSystemUIProxy(this);
170     }
171 
172     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)173     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
174         pw.println("  user:" + mContext.getUserId());
175         pw.println("  shouldRegisterCarSystemUiProxy:" + shouldRegisterCarSystemUIProxy(mContext));
176         pw.println("  mConnected:" + mConnected);
177         pw.println("  mRemoteCarTaskViewServerSet size:" + mRemoteCarTaskViewServerSet.size());
178         pw.println("  mRemoteCarTaskViewServerSet:");
179         for (RemoteCarTaskViewServerImpl remoteCarTaskViewServer : mRemoteCarTaskViewServerSet) {
180             pw.println("    " + remoteCarTaskViewServer);
181         }
182     }
183 
removeExistingTaskViewTasks()184     private void removeExistingTaskViewTasks() {
185         Display[] displays = mDisplayManager.getDisplays();
186         for (int i = 0; i < displays.length; i++) {
187             List<ActivityManager.RunningTaskInfo> taskInfos =
188                     mTaskOrganizer.getRunningTasks(displays[i].getDisplayId());
189             removeMultiWindowTasks(taskInfos);
190         }
191     }
192 
removeMultiWindowTasks(List<ActivityManager.RunningTaskInfo> taskInfos)193     private static void removeMultiWindowTasks(List<ActivityManager.RunningTaskInfo> taskInfos) {
194         ActivityTaskManager atm = ActivityTaskManager.getInstance();
195         for (ActivityManager.RunningTaskInfo taskInfo : taskInfos) {
196             // In Auto, only TaskView tasks have WINDOWING_MODE_MULTI_WINDOW as of now.
197             if (taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
198                 Slog.d(TAG, "Found a dangling task, removing: " + taskInfo.taskId);
199                 atm.removeTask(taskInfo.taskId);
200             }
201         }
202     }
203 
204     /**
205      * Checks the permission of the calling process. Throws {@link SecurityException} if
206      * {@link Car#PERMISSION_MANAGE_CAR_SYSTEM_UI} is not granted.
207      */
ensureManageSystemUIPermission(Context context)208     public static void ensureManageSystemUIPermission(Context context) {
209         if (Binder.getCallingPid() == android.os.Process.myPid()) {
210             // If called from within CarSystemUI, allow.
211             return;
212         }
213         if (context.checkCallingPermission(Car.PERMISSION_MANAGE_CAR_SYSTEM_UI)
214                 == PackageManager.PERMISSION_GRANTED) {
215             return;
216         }
217         throw new SecurityException("requires permission " + Car.PERMISSION_MANAGE_CAR_SYSTEM_UI);
218     }
219 }
220