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 49 import java.io.PrintWriter; 50 import java.util.List; 51 52 import javax.inject.Inject; 53 54 /** 55 * This class provides a concrete implementation for {@link CarSystemUIProxy}. It hosts all the 56 * system ui interaction that is required by other apps. 57 */ 58 @WMSingleton 59 public final class CarSystemUIProxyImpl 60 implements CarSystemUIProxy, CarServiceProvider.CarServiceOnConnectedListener, Dumpable { 61 private static final String TAG = CarSystemUIProxyImpl.class.getSimpleName(); 62 63 private final Context mContext; 64 private final SyncTransactionQueue mSyncQueue; 65 private final ShellTaskOrganizer mTaskOrganizer; 66 private final TaskViewTransitions mTaskViewTransitions; 67 private final ArraySet<RemoteCarTaskViewServerImpl> mRemoteCarTaskViewServerSet = 68 new ArraySet<>(); 69 private final DisplayManager mDisplayManager; 70 71 private boolean mConnected; 72 private CarActivityManager mCarActivityManager; 73 74 /** 75 * Returns true if {@link CarSystemUIProxyImpl} should be registered, false otherwise. 76 * This could be false because of reasons like: 77 * <ul> 78 * <li>Current user is not a system user.</li> 79 * <li>Or {@code config_registerCarSystemUIProxy} is disabled.</li> 80 * </ul> 81 */ shouldRegisterCarSystemUIProxy(Context context)82 public static boolean shouldRegisterCarSystemUIProxy(Context context) { 83 if (!Process.myUserHandle().isSystem()) { 84 Slog.i(TAG, "Non system user."); 85 return false; 86 } 87 if (!context.getResources().getBoolean(R.bool.config_registerCarSystemUIProxy)) { 88 Slog.i(TAG, "config_registerCarSystemUIProxy disabled"); 89 return false; 90 } 91 return true; 92 } 93 94 @Inject CarSystemUIProxyImpl( Context context, CarServiceProvider carServiceProvider, SyncTransactionQueue syncTransactionQueue, ShellTaskOrganizer taskOrganizer, TaskViewTransitions taskViewTransitions, DumpManager dumpManager)95 CarSystemUIProxyImpl( 96 Context context, 97 CarServiceProvider carServiceProvider, 98 SyncTransactionQueue syncTransactionQueue, 99 ShellTaskOrganizer taskOrganizer, 100 TaskViewTransitions taskViewTransitions, 101 DumpManager dumpManager) { 102 mContext = context; 103 mTaskOrganizer = taskOrganizer; 104 mSyncQueue = syncTransactionQueue; 105 mTaskViewTransitions = taskViewTransitions; 106 mDisplayManager = mContext.getSystemService(DisplayManager.class); 107 dumpManager.registerDumpable(this); 108 109 if (!shouldRegisterCarSystemUIProxy(mContext)) { 110 Slog.i(TAG, "Not registering CarSystemUIProxy."); 111 return; 112 } 113 carServiceProvider.addListener(this); 114 } 115 116 /** Returns true if a taskview with a launch root task exists on {@code displayId}. */ isLaunchRootTaskPresent(int displayId)117 public boolean isLaunchRootTaskPresent(int displayId) { 118 for (RemoteCarTaskViewServerImpl remoteCarTaskViewServer : mRemoteCarTaskViewServerSet) { 119 if (remoteCarTaskViewServer.hasLaunchRootTaskOnDisplay(displayId)) { 120 return true; 121 } 122 } 123 return false; 124 } 125 126 /** Returns the list of all the task views. */ getAllTaskViews()127 public ArraySet<RemoteCarTaskViewServerImpl> getAllTaskViews() { 128 return mRemoteCarTaskViewServerSet; 129 } 130 131 @Override createControlledCarTaskView(CarTaskViewClient carTaskViewClient)132 public CarTaskViewHost createControlledCarTaskView(CarTaskViewClient carTaskViewClient) { 133 return createCarTaskView(carTaskViewClient); 134 } 135 136 @Override createCarTaskView(CarTaskViewClient carTaskViewClient)137 public CarTaskViewHost createCarTaskView(CarTaskViewClient carTaskViewClient) { 138 ensureManageSystemUIPermission(mContext); 139 RemoteCarTaskViewServerImpl remoteCarTaskViewServerImpl = 140 new RemoteCarTaskViewServerImpl( 141 mContext, 142 mTaskOrganizer, 143 mSyncQueue, 144 carTaskViewClient, 145 this, 146 mTaskViewTransitions, mCarActivityManager); 147 mRemoteCarTaskViewServerSet.add(remoteCarTaskViewServerImpl); 148 return remoteCarTaskViewServerImpl.getHostImpl(); 149 } 150 151 /** Clears the taskview from the internal state. */ onCarTaskViewReleased(RemoteCarTaskViewServerImpl remoteCarTaskViewServer)152 public void onCarTaskViewReleased(RemoteCarTaskViewServerImpl remoteCarTaskViewServer) { 153 mRemoteCarTaskViewServerSet.remove(remoteCarTaskViewServer); 154 } 155 156 @Override onConnected(Car car)157 public void onConnected(Car car) { 158 mConnected = true; 159 removeExistingTaskViewTasks(); 160 161 mCarActivityManager = car.getCarManager(CarActivityManager.class); 162 mCarActivityManager.registerTaskMonitor(); 163 mCarActivityManager.registerCarSystemUIProxy(this); 164 } 165 166 @Override dump(@onNull PrintWriter pw, @NonNull String[] args)167 public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { 168 pw.println(" user:" + mContext.getUserId()); 169 pw.println(" shouldRegisterCarSystemUiProxy:" + shouldRegisterCarSystemUIProxy(mContext)); 170 pw.println(" mConnected:" + mConnected); 171 pw.println(" mRemoteCarTaskViewServerSet size:" + mRemoteCarTaskViewServerSet.size()); 172 pw.println(" mRemoteCarTaskViewServerSet:"); 173 for (RemoteCarTaskViewServerImpl remoteCarTaskViewServer : mRemoteCarTaskViewServerSet) { 174 pw.println(" " + remoteCarTaskViewServer); 175 } 176 } 177 removeExistingTaskViewTasks()178 private void removeExistingTaskViewTasks() { 179 Display[] displays = mDisplayManager.getDisplays(); 180 for (int i = 0; i < displays.length; i++) { 181 List<ActivityManager.RunningTaskInfo> taskInfos = 182 mTaskOrganizer.getRunningTasks(displays[i].getDisplayId()); 183 removeMultiWindowTasks(taskInfos); 184 } 185 } 186 removeMultiWindowTasks(List<ActivityManager.RunningTaskInfo> taskInfos)187 private static void removeMultiWindowTasks(List<ActivityManager.RunningTaskInfo> taskInfos) { 188 ActivityTaskManager atm = ActivityTaskManager.getInstance(); 189 for (ActivityManager.RunningTaskInfo taskInfo : taskInfos) { 190 // In Auto, only TaskView tasks have WINDOWING_MODE_MULTI_WINDOW as of now. 191 if (taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { 192 Slog.d(TAG, "Found a dangling task, removing: " + taskInfo.taskId); 193 atm.removeTask(taskInfo.taskId); 194 } 195 } 196 } 197 198 /** 199 * Checks the permission of the calling process. Throws {@link SecurityException} if 200 * {@link Car#PERMISSION_MANAGE_CAR_SYSTEM_UI} is not granted. 201 */ ensureManageSystemUIPermission(Context context)202 public static void ensureManageSystemUIPermission(Context context) { 203 if (Binder.getCallingPid() == android.os.Process.myPid()) { 204 // If called from within CarSystemUI, allow. 205 return; 206 } 207 if (context.checkCallingPermission(Car.PERMISSION_MANAGE_CAR_SYSTEM_UI) 208 == PackageManager.PERMISSION_GRANTED) { 209 return; 210 } 211 throw new SecurityException("requires permission " + Car.PERMISSION_MANAGE_CAR_SYSTEM_UI); 212 } 213 } 214