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