• 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 
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 
22 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
23 
24 import android.Manifest;
25 import android.annotation.MainThread;
26 import android.annotation.NonNull;
27 import android.annotation.RequiresApi;
28 import android.annotation.RequiresPermission;
29 import android.annotation.SystemApi;
30 import android.annotation.UiContext;
31 import android.app.Activity;
32 import android.car.Car;
33 import android.car.annotation.ApiRequirements;
34 import android.car.builtin.util.Slogf;
35 import android.content.Context;
36 import android.os.Build;
37 import android.os.RemoteException;
38 import android.os.UserManager;
39 import android.util.Log;
40 
41 import java.util.ArrayList;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.concurrent.Executor;
45 
46 /**
47  * This class is used for creating task views & is created on a per activity basis.
48  * @hide
49  */
50 @SystemApi
51 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
52 public final class CarTaskViewController {
53     private static final String TAG = CarTaskViewController.class.getSimpleName();
54     static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
55 
56     private final ICarSystemUIProxy mService;
57     private final Context mHostContext;
58     private final CarTaskViewControllerHostLifecycle mLifecycle;
59     private final List<RemoteCarTaskView> mRemoteCarTaskViews =
60             new ArrayList<>();
61     private final CarTaskViewInputInterceptor mTaskViewInputInterceptor;
62     private final ICarActivityService mCarActivityService;
63 
64     private boolean mReleased = false;
65 
66     /**
67      * @param service the binder interface to communicate with the car system UI.
68      * @hide
69      */
CarTaskViewController(@iContext Context hostContext, @NonNull CarTaskViewControllerHostLifecycle lifecycle, @NonNull ICarSystemUIProxy service, ICarActivityService carActivityService)70     CarTaskViewController(@UiContext Context hostContext,
71             @NonNull CarTaskViewControllerHostLifecycle lifecycle,
72             @NonNull ICarSystemUIProxy service,
73             ICarActivityService carActivityService) {
74         mHostContext = hostContext;
75         mService = service;
76         mLifecycle = lifecycle;
77         mCarActivityService = carActivityService;
78         mTaskViewInputInterceptor = new CarTaskViewInputInterceptor(hostContext, lifecycle, this);
79     }
80 
81     /**
82      * Creates a new {@link ControlledRemoteCarTaskView}.
83      *
84      * @param callbackExecutor the executor to get the {@link ControlledRemoteCarTaskViewCallback}
85      *                         on.
86      * @param controlledRemoteCarTaskViewCallback the callback to monitor the
87      *                                            {@link ControlledRemoteCarTaskView} related
88      *                                            events.
89      */
90     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
91             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
92     @RequiresPermission(allOf = {Manifest.permission.INJECT_EVENTS,
93             Manifest.permission.INTERNAL_SYSTEM_WINDOW}, conditional = true)
94     @MainThread
createControlledRemoteCarTaskView( @onNull ControlledRemoteCarTaskViewConfig controlledRemoteCarTaskViewConfig, @NonNull Executor callbackExecutor, @NonNull ControlledRemoteCarTaskViewCallback controlledRemoteCarTaskViewCallback)95     public void createControlledRemoteCarTaskView(
96             @NonNull ControlledRemoteCarTaskViewConfig controlledRemoteCarTaskViewConfig,
97             @NonNull Executor callbackExecutor,
98             @NonNull ControlledRemoteCarTaskViewCallback controlledRemoteCarTaskViewCallback) {
99         assertPlatformVersionAtLeastU();
100         if (mReleased) {
101             throw new IllegalStateException("CarTaskViewController is already released");
102         }
103         ControlledRemoteCarTaskView taskViewClient =
104                 new ControlledRemoteCarTaskView(
105                         mHostContext,
106                         controlledRemoteCarTaskViewConfig,
107                         callbackExecutor,
108                         controlledRemoteCarTaskViewCallback,
109                         /* carTaskViewController= */ this,
110                         mHostContext.getSystemService(UserManager.class));
111 
112         try {
113             ICarTaskViewHost host = mService.createControlledCarTaskView(
114                     taskViewClient.mICarTaskViewClient);
115             taskViewClient.setRemoteHost(host);
116             mRemoteCarTaskViews.add(taskViewClient);
117 
118             if (controlledRemoteCarTaskViewConfig.mShouldCaptureGestures
119                     || controlledRemoteCarTaskViewConfig.mShouldCaptureLongPress) {
120                 assertPermission(Manifest.permission.INJECT_EVENTS);
121                 assertPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW);
122                 mTaskViewInputInterceptor.init();
123             }
124         } catch (RemoteException e) {
125             Slogf.e(TAG, "Unable to create task view.", e);
126         }
127     }
128 
129     /**
130      * Creates a new {@link RemoteCarRootTaskView}.
131      *
132      * @param callbackExecutor the executor to get the {@link RemoteCarRootTaskViewCallback} on.
133      * @param remoteCarRootTaskViewCallback the callback to monitor the
134      *                                      {@link RemoteCarRootTaskView} related events.
135      * @hide
136      */
137     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
138             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
139     @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
140     @MainThread
createRemoteCarRootTaskView( @onNull RemoteCarRootTaskViewConfig remoteCarRootTaskViewConfig, @NonNull Executor callbackExecutor, @NonNull RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback)141     public void createRemoteCarRootTaskView(
142             @NonNull RemoteCarRootTaskViewConfig remoteCarRootTaskViewConfig,
143             @NonNull Executor callbackExecutor,
144             @NonNull RemoteCarRootTaskViewCallback remoteCarRootTaskViewCallback) {
145         assertPlatformVersionAtLeastU();
146         assertPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH);
147         if (mReleased) {
148             throw new IllegalStateException("CarTaskViewController is already released");
149         }
150         RemoteCarRootTaskView taskViewClient =
151                 new RemoteCarRootTaskView(
152                         mHostContext,
153                         remoteCarRootTaskViewConfig,
154                         callbackExecutor,
155                         remoteCarRootTaskViewCallback,
156                         /* carTaskViewController= */ this,
157                         mCarActivityService
158                 );
159 
160         try {
161             ICarTaskViewHost host = mService.createCarTaskView(taskViewClient.mICarTaskViewClient);
162             taskViewClient.setRemoteHost(host);
163             mRemoteCarTaskViews.add(taskViewClient);
164         } catch (RemoteException e) {
165             Slogf.e(TAG, "Unable to create root task view.", e);
166         }
167     }
168 
169     /**
170      * Creates a new {@link RemoteCarDefaultRootTaskView}.
171      *
172      * @param callbackExecutor the executor to get the {@link RemoteCarDefaultRootTaskViewCallback}
173      *                         on.
174      * @param remoteCarDefaultRootTaskViewCallback the callback to monitor the
175      *                                             {@link RemoteCarDefaultRootTaskView} related
176      *                                             events.
177      * @hide
178      */
179     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_1,
180             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_1)
181     @MainThread
createRemoteCarDefaultRootTaskView( @onNull RemoteCarDefaultRootTaskViewConfig remoteCarDefaultRootTaskViewConfig, @NonNull Executor callbackExecutor, @NonNull RemoteCarDefaultRootTaskViewCallback remoteCarDefaultRootTaskViewCallback)182     public void createRemoteCarDefaultRootTaskView(
183             @NonNull RemoteCarDefaultRootTaskViewConfig remoteCarDefaultRootTaskViewConfig,
184             @NonNull Executor callbackExecutor,
185             @NonNull RemoteCarDefaultRootTaskViewCallback remoteCarDefaultRootTaskViewCallback) {
186         assertPlatformVersionAtLeastU();
187         if (mReleased) {
188             throw new IllegalStateException("CarTaskViewController is already released");
189         }
190         RemoteCarDefaultRootTaskView taskViewClient =
191                 new RemoteCarDefaultRootTaskView(
192                         mHostContext,
193                         remoteCarDefaultRootTaskViewConfig,
194                         callbackExecutor,
195                         remoteCarDefaultRootTaskViewCallback,
196                         /* carTaskViewController= */ this
197                 );
198 
199         try {
200             ICarTaskViewHost host = mService.createCarTaskView(
201                     taskViewClient.mICarTaskViewClient);
202             taskViewClient.setRemoteHost(host);
203             mRemoteCarTaskViews.add(taskViewClient);
204         } catch (RemoteException e) {
205             Slogf.e(TAG, "Unable to create default root task view.", e);
206         }
207     }
208 
onRemoteCarTaskViewReleased(@onNull RemoteCarTaskView taskView)209     void onRemoteCarTaskViewReleased(@NonNull RemoteCarTaskView taskView) {
210         if (mReleased) {
211             Log.w(TAG, "Failed to remove the taskView as the "
212                     + "CarTaskViewController is already released");
213             return;
214         }
215         if (!mRemoteCarTaskViews.contains(taskView)) {
216             Log.w(TAG, "This taskView has already been removed");
217             return;
218         }
219         mRemoteCarTaskViews.remove(taskView);
220     }
221 
assertPermission(String permission)222     private void assertPermission(String permission) {
223         if (mHostContext.checkCallingOrSelfPermission(permission)
224                 != PERMISSION_GRANTED) {
225             throw new SecurityException("requires " + permission);
226         }
227     }
228 
229     /**
230      * Releases all the resources held by the taskviews associated with this controller.
231      *
232      * <p> Once {@link #release()} is called, the current instance of {@link CarTaskViewController}
233      * cannot be used further. A new instance should be requested using
234      * {@link CarActivityManager#getCarTaskViewController(Activity, Executor,
235      * CarTaskViewControllerCallback)}.
236      */
237     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
238             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
239     @MainThread
release()240     public void release() {
241         assertPlatformVersionAtLeastU();
242         if (mReleased) {
243             Slogf.w(TAG, "CarTaskViewController is already released");
244             return;
245         }
246         releaseTaskViews();
247         mTaskViewInputInterceptor.release();
248         mReleased = true;
249     }
250 
251     @MainThread
releaseTaskViews()252     void releaseTaskViews() {
253         Iterator<RemoteCarTaskView> iterator = mRemoteCarTaskViews.iterator();
254         while (iterator.hasNext()) {
255             RemoteCarTaskView taskView = iterator.next();
256             // Remove the task view here itself because release triggers removal again which can
257             // result in concurrent modification exception.
258             iterator.remove();
259             taskView.release();
260         }
261     }
262 
263     /**
264      * Brings all the embedded tasks to the front.
265      */
266     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
267             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
268     @MainThread
showEmbeddedTasks()269     public void showEmbeddedTasks() {
270         assertPlatformVersionAtLeastU();
271         if (mReleased) {
272             throw new IllegalStateException("CarTaskViewController is already released");
273         }
274         for (int i = 0, length = mRemoteCarTaskViews.size(); i < length; i++) {
275             RemoteCarTaskView carTaskView = mRemoteCarTaskViews.get(i);
276             // TODO(b/267314188): Add a new method in ICarSystemUI to call
277             // showEmbeddedTask in a single WCT for multiple tasks.
278             carTaskView.showEmbeddedTask();
279         }
280     }
281 
isHostVisible()282     boolean isHostVisible() {
283         return mLifecycle.isVisible();
284     }
285 
getRemoteCarTaskViews()286     List<RemoteCarTaskView> getRemoteCarTaskViews() {
287         return mRemoteCarTaskViews;
288     }
289 }
290