• 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 static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU;
20 
21 import android.annotation.MainThread;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresApi;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemApi;
27 import android.app.ActivityManager;
28 import android.app.ActivityOptions;
29 import android.app.PendingIntent;
30 import android.car.Car;
31 import android.car.annotation.ApiRequirements;
32 import android.car.builtin.util.Slogf;
33 import android.car.builtin.view.ViewHelper;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.graphics.Rect;
37 import android.os.Build;
38 import android.graphics.Region;
39 import android.os.UserManager;
40 import android.view.Display;
41 import android.view.SurfaceControl;
42 
43 import java.util.concurrent.Executor;
44 
45 /**
46  * A {@link ControlledRemoteCarTaskView} should be used when the launch intent of the task is known
47  * before hand.
48  *
49  * The underlying task will be restarted if it is crashed depending on the
50  * {@code autoRestartOnCrash}.
51  *
52  * <p>It should be preferred when:
53  * <ul>
54  *     <li>The underlying task is meant to be started by the host and be there forever.</li>
55  * </ul>
56  *
57  * @hide
58  */
59 @SystemApi
60 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
61 public final class ControlledRemoteCarTaskView extends RemoteCarTaskView {
62     private static final String TAG = ControlledRemoteCarTaskView.class.getSimpleName();
63 
64     private final Executor mCallbackExecutor;
65     private final ControlledRemoteCarTaskViewCallback mCallback;
66     private final UserManager mUserManager;
67     private final CarTaskViewController mCarTaskViewController;
68     private final Context mContext;
69     private final ControlledRemoteCarTaskViewConfig mConfig;
70     private final Rect mTmpRect = new Rect();
71 
72     private ActivityManager.RunningTaskInfo mTaskInfo;
73 
74     final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() {
75         @Override
76         public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
77             mTaskInfo = taskInfo;
78             updateWindowBounds();
79             if (taskInfo.taskDescription != null) {
80                 ViewHelper.seResizeBackgroundColor(
81                         ControlledRemoteCarTaskView.this,
82                         taskInfo.taskDescription.getBackgroundColor());
83             }
84             ControlledRemoteCarTaskView.this.onTaskAppeared(taskInfo, leash);
85         }
86 
87         @Override
88         public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
89             if (taskInfo.taskDescription != null) {
90                 ViewHelper.seResizeBackgroundColor(
91                         ControlledRemoteCarTaskView.this,
92                         taskInfo.taskDescription.getBackgroundColor());
93             }
94             ControlledRemoteCarTaskView.this.onTaskInfoChanged(taskInfo);
95         }
96 
97         @Override
98         public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
99             mTaskInfo = null;
100             ControlledRemoteCarTaskView.this.onTaskVanished(taskInfo);
101         }
102 
103         @Override
104         public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) {
105             ViewHelper.seResizeBackgroundColor(ControlledRemoteCarTaskView.this, color);
106         }
107 
108         @Override
109         public Rect getCurrentBoundsOnScreen() {
110             ViewHelper.getBoundsOnScreen(ControlledRemoteCarTaskView.this, mTmpRect);
111             return mTmpRect;
112         }
113     };
114 
ControlledRemoteCarTaskView( @onNull Context context, ControlledRemoteCarTaskViewConfig config, @NonNull Executor callbackExecutor, @NonNull ControlledRemoteCarTaskViewCallback callback, CarTaskViewController carTaskViewController, @NonNull UserManager userManager)115     ControlledRemoteCarTaskView(
116             @NonNull Context context,
117             ControlledRemoteCarTaskViewConfig config,
118             @NonNull Executor callbackExecutor,
119             @NonNull ControlledRemoteCarTaskViewCallback callback,
120             CarTaskViewController carTaskViewController,
121             @NonNull UserManager userManager) {
122         super(context);
123         mContext = context;
124         mConfig = config;
125         mCallbackExecutor = callbackExecutor;
126         mCallback = callback;
127         mCarTaskViewController = carTaskViewController;
128         mUserManager = userManager;
129 
130         mCallbackExecutor.execute(() -> mCallback.onTaskViewCreated(this));
131     }
132 
133     /**
134      * Starts the underlying activity, specified as part of
135      * {@link CarTaskViewController#createControlledRemoteCarTaskView(Executor, Intent, boolean,
136      * ControlledRemoteCarTaskViewCallback)}.
137      */
138     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
139     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
140             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
141     @MainThread
startActivity()142     public void startActivity() {
143         assertPlatformVersionAtLeastU();
144         if (!mUserManager.isUserUnlocked()) {
145             if (CarTaskViewController.DBG) {
146                 Slogf.d(TAG, "Can't start activity due to user is isn't unlocked");
147             }
148             return;
149         }
150 
151         // Don't start activity when the display is off. This can happen when the taskview is not
152         // attached to a window.
153         if (getDisplay() == null) {
154             Slogf.w(TAG, "Can't start activity because display is not available in "
155                     + "taskview yet.");
156             return;
157         }
158         // Don't start activity when the display is off for ActivityVisibilityTests.
159         if (getDisplay().getState() != Display.STATE_ON) {
160             Slogf.w(TAG, "Can't start activity due to the display is off");
161             return;
162         }
163 
164         ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext,
165                 /* enterResId= */ 0, /* exitResId= */ 0);
166         Rect launchBounds = new Rect();
167         ViewHelper.getBoundsOnScreen(this, launchBounds);
168         launchBounds.set(launchBounds);
169         if (CarTaskViewController.DBG) {
170             Slogf.d(TAG, "Starting (" + mConfig.mActivityIntent.getComponent() + ") on "
171                     + launchBounds);
172         }
173         Intent fillInIntent = null;
174         if ((mConfig.mActivityIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
175             fillInIntent = new Intent().addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
176         }
177         startActivity(
178                 PendingIntent.getActivity(mContext, /* requestCode= */ 0,
179                         mConfig.mActivityIntent,
180                         PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT),
181                 fillInIntent, options, launchBounds);
182     }
183 
184     @Override
onInitialized()185     void onInitialized() {
186         mContext.getMainExecutor().execute(() -> {
187             startActivity();
188         });
189         mCallbackExecutor.execute(() -> mCallback.onTaskViewInitialized());
190     }
191 
192     @Override
onReleased()193     void onReleased() {
194         mTaskInfo = null;
195         mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased());
196         mCarTaskViewController.onRemoteCarTaskViewReleased(this);
197     }
198 
199     @Override
onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)200     void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
201         super.onTaskAppeared(taskInfo, leash);
202         mCallbackExecutor.execute(() -> mCallback.onTaskAppeared(taskInfo));
203     }
204 
205     @Override
onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)206     void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
207         super.onTaskInfoChanged(taskInfo);
208         mCallbackExecutor.execute(() -> mCallback.onTaskInfoChanged(taskInfo));
209     }
210 
211     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
212     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
213             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
214     @Override
215     @MainThread
showEmbeddedTask()216     public void showEmbeddedTask() {
217         assertPlatformVersionAtLeastU();
218         super.showEmbeddedTask();
219         if (getTaskInfo() == null) {
220             if (CarTaskViewController.DBG) {
221                 Slogf.d(TAG, "Embedded task not available, starting it now.");
222             }
223             startActivity();
224             return;
225         }
226     }
227 
228     @Override
onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)229     void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
230         super.onTaskVanished(taskInfo);
231         if (mConfig.mShouldAutoRestartOnTaskRemoval && mCarTaskViewController.isHostVisible()) {
232             // onTaskVanished can be called when the host is in the background. In this case
233             // embedded activity should not be started.
234             Slogf.i(TAG, "Restarting task " + taskInfo.baseActivity
235                     + " in ControlledCarTaskView");
236             startActivity();
237         }
238         mCallbackExecutor.execute(() -> mCallback.onTaskVanished(taskInfo));
239     }
240 
241     /**
242      * @return the {@link android.app.ActivityManager.RunningTaskInfo} of the task currently
243      * running in the TaskView.
244      */
245     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
246             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
247     @MainThread
248     @Override
249     @Nullable
getTaskInfo()250     public ActivityManager.RunningTaskInfo getTaskInfo() {
251         return mTaskInfo;
252     }
253 
getConfig()254     ControlledRemoteCarTaskViewConfig getConfig() {
255         return mConfig;
256     }
257 
258     @Override
toString()259     public String toString() {
260         return toString(/* withBounds= */ false);
261     }
262 
toString(boolean withBounds)263     String toString(boolean withBounds) {
264         if (withBounds) {
265             ViewHelper.getBoundsOnScreen(this, mTmpRect);
266         }
267         return TAG + " {\n"
268                 + "  config=" + mConfig + "\n"
269                 + "  taskId=" + (getTaskInfo() == null ? "null" : getTaskInfo().taskId) + "\n"
270                 + (withBounds ? ("  boundsOnScreen=" + mTmpRect) : "")
271                 + "}\n";
272     }
273 
274     // Since SurfaceView is public, these methods need to be overridden. Details in b/296680464.
275     @Override
276     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
277     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
278             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
279     @MainThread
addInsets(int index, int type, @NonNull Rect frame)280     public void addInsets(int index, int type, @NonNull Rect frame) {
281         super.addInsets(index, type, frame);
282     }
283 
284     @Override
285     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
286     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
287             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
removeInsets(int index, int type)288     public void removeInsets(int index, int type) {
289         super.removeInsets(index, type);
290     }
291 
292     @Override
293     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
294             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
295     @MainThread
release()296     public void release() {
297         super.release();
298     }
299 
300     @Override
301     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
302             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
onAttachedToWindow()303     public void onAttachedToWindow() {
304         super.onAttachedToWindow();
305     }
306 
307 
308     @Override
309     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
310             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
onDetachedFromWindow()311     public void onDetachedFromWindow() {
312         super.onDetachedFromWindow();
313     }
314 
315     @Override
316     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
317             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
318     @MainThread
isInitialized()319     public boolean isInitialized() {
320         return super.isInitialized();
321     }
322 
323     @Override
324     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
325             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
326     @MainThread
setObscuredTouchRegion(@onNull Region obscuredRegion)327     public void setObscuredTouchRegion(@NonNull Region obscuredRegion) {
328         super.setObscuredTouchRegion(obscuredRegion);
329     }
330 
331     @Override
332     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
333             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
334     @MainThread
setObscuredTouchRect(@onNull Rect obscuredRect)335     public void setObscuredTouchRect(@NonNull Rect obscuredRect) {
336         super.setObscuredTouchRect(obscuredRect);
337     }
338 
339     @Override
340     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
341     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
342             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
343     @MainThread
updateWindowBounds()344     public void updateWindowBounds() {
345         super.updateWindowBounds();
346     }
347 }
348