/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car.app; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.car.builtin.util.Slogf; import android.car.builtin.view.ViewHelper; import android.content.Context; import android.graphics.Rect; import android.os.Binder; import android.view.SurfaceControl; import com.android.internal.annotations.GuardedBy; import java.util.concurrent.Executor; /** * A {@link RemoteCarDefaultRootTaskView} can act as a default app container. A default app * container is the container where all apps open by default. * * Please use this with caution. If this task view is used inside an activity, that activity might * always remain running as all the other activities will start appearing inside this launch root * instead of appearing in the full screen. * * @hide */ public final class RemoteCarDefaultRootTaskView extends RemoteCarTaskView { private static final String TAG = RemoteCarDefaultRootTaskView.class.getSimpleName(); private final Executor mCallbackExecutor; private final RemoteCarDefaultRootTaskViewCallback mCallback; private final CarTaskViewController mCarTaskViewController; private final RemoteCarDefaultRootTaskViewConfig mConfig; private final Rect mTmpRect = new Rect(); private final Object mLock = new Object(); @GuardedBy("mLock") private final RootTaskStackManager mRootTaskStackManager = new RootTaskStackManager(); @GuardedBy("mLock") private ActivityManager.RunningTaskInfo mRootTask; final ICarTaskViewClient mICarTaskViewClient = new ICarTaskViewClient.Stub() { @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (mLock) { if (mRootTask == null) { mRootTask = taskInfo; // If onTaskAppeared() is called, it implicitly means that super.isInitialized() // is true, as the root task is created only after initialization. long identity = Binder.clearCallingIdentity(); try { mCallbackExecutor.execute(() -> { // Check for isReleased() because the car task view might have // already been released but this code path is executed later because // the executor was busy. if (isReleased()) { Slogf.w(TAG, "car task view has already been released"); return; } mCallback.onTaskViewInitialized(); }); } finally { Binder.restoreCallingIdentity(identity); } if (taskInfo.taskDescription != null) { ViewHelper.seResizeBackgroundColor( RemoteCarDefaultRootTaskView.this, taskInfo.taskDescription.getBackgroundColor()); } updateWindowBounds(); } mRootTaskStackManager.taskAppeared(taskInfo, leash); } long identity = Binder.clearCallingIdentity(); try { mCallbackExecutor.execute(() -> { if (isReleased()) { Slogf.w(TAG, "car task view has already been released"); return; } mCallback.onTaskAppeared(taskInfo); }); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { synchronized (mLock) { if (mRootTask == null) { return; } if (mRootTask.taskId == taskInfo.taskId && taskInfo.taskDescription != null) { ViewHelper.seResizeBackgroundColor( RemoteCarDefaultRootTaskView.this, taskInfo.taskDescription.getBackgroundColor()); } mRootTaskStackManager.taskInfoChanged(taskInfo); } long identity = Binder.clearCallingIdentity(); try { mCallbackExecutor.execute(() -> { if (isReleased()) { Slogf.w(TAG, "car task view has already been released"); return; } mCallback.onTaskInfoChanged(taskInfo); }); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { synchronized (mLock) { if (mRootTask == null) { return; } if (mRootTask.taskId == taskInfo.taskId) { mRootTask = null; } mRootTaskStackManager.taskVanished(taskInfo); } long identity = Binder.clearCallingIdentity(); try { mCallbackExecutor.execute(() -> { if (isReleased()) { Slogf.w(TAG, "car task view has already been released"); return; } mCallback.onTaskVanished(taskInfo); }); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setResizeBackgroundColor(SurfaceControl.Transaction t, int color) { ViewHelper.seResizeBackgroundColor(RemoteCarDefaultRootTaskView.this, color); } @Override public Rect getCurrentBoundsOnScreen() { ViewHelper.getBoundsOnScreen(RemoteCarDefaultRootTaskView.this, mTmpRect); return mTmpRect; } }; RemoteCarDefaultRootTaskView( @NonNull Context context, RemoteCarDefaultRootTaskViewConfig config, @NonNull Executor callbackExecutor, @NonNull RemoteCarDefaultRootTaskViewCallback callback, CarTaskViewController carTaskViewController) { super(context); mConfig = config; mCallbackExecutor = callbackExecutor; mCallback = callback; mCarTaskViewController = carTaskViewController; mCallbackExecutor.execute(() -> mCallback.onTaskViewCreated(this)); } /** * Returns the task info of the top task running in the root task embedded in this task view. * * @return task info object of the top task. */ @Nullable public ActivityManager.RunningTaskInfo getTopTaskInfo() { synchronized (mLock) { return mRootTaskStackManager.getTopTask(); } } @Override void onInitialized() { // A signal when the surface and host-side are ready. This task view initialization // completes after root task has been created and set as launch root. createLaunchRootTask(mConfig.getDisplayId(), mConfig.embedsHomeTask(), mConfig.embedsRecentsTask(), mConfig.embedsAssistantTask()); } @Override public boolean isInitialized() { synchronized (mLock) { return super.isInitialized() && mRootTask != null; } } @Override void onReleased() { mCallbackExecutor.execute(() -> mCallback.onTaskViewReleased()); mCarTaskViewController.onRemoteCarTaskViewReleased(this); } @Nullable @Override public ActivityManager.RunningTaskInfo getTaskInfo() { synchronized (mLock) { return mRootTask; } } RemoteCarDefaultRootTaskViewConfig getConfig() { return mConfig; } @Override public String toString() { return toString(/* withBounds= */ false); } String toString(boolean withBounds) { if (withBounds) { ViewHelper.getBoundsOnScreen(this, mTmpRect); } return TAG + " {\n" + " config=" + mConfig + "\n" + " rootTaskId=" + (getTaskInfo() == null ? "null" : getTaskInfo().taskId) + "\n" + (withBounds ? (" boundsOnScreen=" + mTmpRect) : "") + "}\n"; } }