• 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 android.car.app;
18 
19 import android.annotation.MainThread;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresApi;
23 import android.annotation.RequiresPermission;
24 import android.app.ActivityManager;
25 import android.car.Car;
26 import android.car.annotation.ApiRequirements;
27 import android.car.builtin.app.TaskInfoHelper;
28 import android.car.builtin.view.ViewHelper;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.graphics.Rect;
32 import android.os.Build;
33 import android.os.IBinder;
34 import android.os.RemoteException;
35 import android.os.ServiceSpecificException;
36 import android.view.SurfaceControl;
37 
38 import com.android.internal.annotations.GuardedBy;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.concurrent.Executor;
43 
44 /**
45  * A {@link RemoteCarRootTaskView} should be used when a given list of activities are required to
46  * appear inside the bounds of a the given {@link android.view.View} in the form of a task stack.
47  * This {@link RemoteCarTaskView} creates a root task and all the given activities will launch
48  * inside that root task.
49  *
50  * <p>It serves these use-cases:
51  * <ul>
52  *     <li>Should be used when the apps that are meant to be in it can be started from anywhere
53  *     in the system. i.e. when the host app has no control over their launching.</li>
54  *     <li>Suitable for apps like Assistant or Setup-Wizard.</li>
55  * </ul>
56  *
57  * @hide
58  */
59 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
60 public final class RemoteCarRootTaskView extends RemoteCarTaskView {
61     private static final String TAG = RemoteCarRootTaskView.class.getSimpleName();
62 
63     private final Executor mCallbackExecutor;
64     private final RemoteCarRootTaskViewCallback mCallback;
65     private final ICarActivityService mCarActivityService;
66     private final CarTaskViewController mCarTaskViewController;
67     private final Rect mTmpRect = new Rect();
68     private final RootTaskStackManager mRootTaskStackManager = new RootTaskStackManager();
69     private final Object mLock = new Object();
70     private final int mDisplayId;
71     /**
72      * List of activities that appear in this {@link RemoteCarRootTaskView}. It's initialized
73      * with the value from {@link RemoteCarRootTaskViewConfig#getAllowListedActivities()} and
74      * can be updated by {@link #updateAllowListedActivities(List)}.
75      */
76     @GuardedBy("mLock")
77     private final ArrayList<ComponentName> mAllowListedActivities;
78 
79     private ActivityManager.RunningTaskInfo mRootTask;
80 
81     final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() {
82         @Override
83         public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
84             if (mRootTask == null) {
85                 mRootTask = taskInfo;
86                 synchronized (mLock) {
87                     setPersistentActivitiesOnRootTask(mAllowListedActivities,
88                             TaskInfoHelper.getToken(taskInfo));
89                 }
90 
91                 // If onTaskAppeared() is called, it implicitly means that super.isInitialized()
92                 // is true, as the root task is created only after initialization.
93                 mCallbackExecutor.execute(() -> mCallback.onTaskViewInitialized());
94 
95                 if (taskInfo.taskDescription != null) {
96                     ViewHelper.seResizeBackgroundColor(
97                             RemoteCarRootTaskView.this,
98                             taskInfo.taskDescription.getBackgroundColor());
99                 }
100                 updateWindowBounds();
101             }
102 
103             mRootTaskStackManager.taskAppeared(taskInfo, leash);
104         }
105 
106         @Override
107         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
108             if (mRootTask.taskId == taskInfo.taskId && taskInfo.taskDescription != null) {
109                 ViewHelper.seResizeBackgroundColor(
110                         RemoteCarRootTaskView.this,
111                         taskInfo.taskDescription.getBackgroundColor());
112             }
113             mRootTaskStackManager.taskInfoChanged(taskInfo);
114         }
115 
116         @Override
117         public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
118             if (mRootTask.taskId == taskInfo.taskId) {
119                 mRootTask = null;
120             }
121             mRootTaskStackManager.taskVanished(taskInfo);
122         }
123 
124         @Override
125         public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) {
126             ViewHelper.seResizeBackgroundColor(RemoteCarRootTaskView.this, color);
127         }
128 
129         @Override
130         public Rect getCurrentBoundsOnScreen() {
131             ViewHelper.getBoundsOnScreen(RemoteCarRootTaskView.this, mTmpRect);
132             return mTmpRect;
133         }
134     };
135 
RemoteCarRootTaskView( @onNull Context context, RemoteCarRootTaskViewConfig config, @NonNull Executor callbackExecutor, @NonNull RemoteCarRootTaskViewCallback callback, CarTaskViewController carTaskViewController, @NonNull ICarActivityService carActivityService)136     RemoteCarRootTaskView(
137             @NonNull Context context,
138             RemoteCarRootTaskViewConfig config,
139             @NonNull Executor callbackExecutor,
140             @NonNull RemoteCarRootTaskViewCallback callback,
141             CarTaskViewController carTaskViewController,
142             @NonNull ICarActivityService carActivityService) {
143         super(context);
144         mCallbackExecutor = callbackExecutor;
145         mCallback = callback;
146         mCarTaskViewController = carTaskViewController;
147         mCarActivityService = carActivityService;
148         synchronized (mLock) {
149             mAllowListedActivities = new ArrayList<>(config.getAllowListedActivities());
150         }
151         mDisplayId = config.getDisplayId();
152 
153         mCallbackExecutor.execute(() -> mCallback.onTaskViewCreated(this));
154     }
155 
156     /**
157      * Returns the task info of the top task running in the root task embedded in this task view.
158      *
159      * @return task info object of the top task.
160      */
161     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
162             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
163     @Nullable
getTopTaskInfo()164     public ActivityManager.RunningTaskInfo getTopTaskInfo() {
165         return mRootTaskStackManager.getTopTask();
166     }
167 
168     @Override
onInitialized()169     void onInitialized() {
170         // A signal when the surface and host-side are ready. This task view initialization
171         // completes after root task has been created.
172         createRootTask(mDisplayId);
173     }
174 
175     @Override
isInitialized()176     public boolean isInitialized() {
177         return super.isInitialized() && mRootTask != null;
178     }
179 
180     @Override
onReleased()181     void onReleased() {
182         mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased());
183         mCarTaskViewController.onRemoteCarTaskViewReleased(this);
184     }
185 
186     @Nullable
187     @Override
getTaskInfo()188     public ActivityManager.RunningTaskInfo getTaskInfo() {
189         return mRootTask;
190     }
191 
192     @Override
toString()193     public String toString() {
194         return toString(/* withBounds= */ false);
195     }
196 
197     /**
198      * Updates the list of activities that appear inside this {@link RemoteCarRootTaskView}.
199      *
200      * <p>Note:
201      * If an activity is already associated with another {@link RemoteCarRootTaskView}, its
202      * designation will be overridden.
203      *
204      * @param list list of {@link ComponentName} of activities to be designated to this
205      *                   {@link RemoteCarRootTaskView}
206      */
207     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
208             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
209     @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
210     @MainThread
updateAllowListedActivities(List<ComponentName> list)211     public void updateAllowListedActivities(List<ComponentName> list) {
212         synchronized (mLock) {
213             if (mRootTask != null) {
214                 List<ComponentName> activitiesToRemove = findDifferences(mAllowListedActivities,
215                         list);
216                 List<ComponentName> activitiesToAdd = findDifferences(list, mAllowListedActivities);
217                 setPersistentActivitiesOnRootTask(activitiesToRemove, /* launchCookie= */ null);
218                 setPersistentActivitiesOnRootTask(activitiesToAdd,
219                         TaskInfoHelper.getToken(mRootTask));
220             }
221 
222             mAllowListedActivities.clear();
223             mAllowListedActivities.addAll(list);
224         }
225     }
226 
227     /**
228      * Returns all the {@link ComponentName}s in {@code firstList} which are not in
229      * {@code secondList}.
230      */
findDifferences(List<ComponentName> firstList, List<ComponentName> secondList)231     private List<ComponentName> findDifferences(List<ComponentName> firstList,
232             List<ComponentName> secondList) {
233         ArrayList<ComponentName> result = new ArrayList<>(firstList);
234         result.removeAll(secondList);
235         return result;
236     }
237 
setPersistentActivitiesOnRootTask(List<ComponentName> activities, IBinder launchCookie)238     private void setPersistentActivitiesOnRootTask(List<ComponentName> activities,
239             IBinder launchCookie) {
240         try {
241             mCarActivityService.setPersistentActivitiesOnRootTask(activities, launchCookie);
242         } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
243             throw e;
244         } catch (ServiceSpecificException e) {
245             throw new IllegalStateException(
246                 "Car service looks crashed on ServiceSpecificException " + e);
247         } catch (RemoteException | RuntimeException e) {
248             throw new IllegalStateException("Car service looks crashed on RemoteException " + e);
249         }
250     }
251 
toString(boolean withBounds)252     String toString(boolean withBounds) {
253         if (withBounds) {
254             ViewHelper.getBoundsOnScreen(this, mTmpRect);
255         }
256 
257         StringBuilder b = new StringBuilder(TAG).append(" {\n");
258 
259         b.append("  mDisplayId=").append(mDisplayId);
260         b.append("\n");
261 
262         b.append("  taskId=").append((getTaskInfo() == null ? "null" : getTaskInfo().taskId));
263         b.append("\n");
264 
265         if (withBounds) {
266             b.append("  boundsOnScreen=").append(mTmpRect);
267             b.append("\n");
268         }
269 
270         b.append("  mAllowListedActivities= [");
271         synchronized (mLock) {
272             for (ComponentName componentName : mAllowListedActivities) {
273                 b.append("\n    ").append(componentName);
274             }
275         }
276         b.append("  ]}\n");
277 
278         return b.toString();
279     }
280 }
281