Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e4f700a authored by Mina Granic's avatar Mina Granic Committed by Android (Google) Code Review
Browse files

Merge "Extract tracking camera policies from CameraStateMonitor." into main

parents f59abc3d 076518a4
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -58,14 +58,20 @@ class AppCompatCameraPolicy {
                DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
                        && DesktopModeHelper.canEnterDesktopMode(wmService.mContext);
        if (needsDisplayRotationCompatPolicy || needsCameraCompatFreeformPolicy) {
            mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH);
            final AppCompatCameraStateSource cameraStateListenerDelegate =
                    new AppCompatCameraStateSource();
            mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH,
                    cameraStateListenerDelegate);
            mActivityRefresher = new ActivityRefresher(wmService, wmService.mH);
            mDisplayRotationCompatPolicy =
                    needsDisplayRotationCompatPolicy ? new DisplayRotationCompatPolicy(
                            displayContent, mCameraStateMonitor, mActivityRefresher) : null;
                            displayContent, mCameraStateMonitor, cameraStateListenerDelegate,
                            mActivityRefresher)
                            : null;
            mCameraCompatFreeformPolicy =
                    needsCameraCompatFreeformPolicy ? new CameraCompatFreeformPolicy(displayContent,
                            mCameraStateMonitor, mActivityRefresher) : null;
                            mCameraStateMonitor, cameraStateListenerDelegate, mActivityRefresher)
                            : null;
        } else {
            mDisplayRotationCompatPolicy = null;
            mCameraCompatFreeformPolicy = null;
@@ -158,7 +164,7 @@ class AppCompatCameraPolicy {
            mCameraCompatFreeformPolicy.dispose();
        }
        if (mCameraStateMonitor != null) {
            mCameraStateMonitor.dispose();
            mCameraStateMonitor.stopListeningToCameraState();
        }
    }

+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.wm;

import android.annotation.NonNull;

/**
 * Interface that camera compat policies to implement to be notified of camera open/close signals.
 */
interface AppCompatCameraStatePolicy {
    /**
     * Notifies the compat listener that a task has opened camera.
     */
    void onCameraOpened(@NonNull ActivityRecord cameraActivity);

    /**
     * Checks whether a listener is ready to do a cleanup when camera is closed.
     *
     * <p>The notifier might try again if false is returned.
     */
    // TODO(b/336474959): try to decouple `cameraId` from the listeners, as the treatment does not
    //  change based on the cameraId - CameraStateMonitor should keep track of this.
    //  This method actually checks "did an activity only temporarily close the camera", because a
    //  refresh for compatibility is triggered.
    boolean canCameraBeClosed(@NonNull String cameraId);

    /**
     * Notifies the compat listener that camera is closed.
     */
    void onCameraClosed();
}
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.wm;

import androidx.annotation.NonNull;

import java.util.ArrayList;

/**
 * Adapter for camera state updates, which notifies all camera policies of camera state changes.
 */
class AppCompatCameraStateSource implements AppCompatCameraStatePolicy {
    private final ArrayList<AppCompatCameraStatePolicy> mCameraStatePolicies = new ArrayList<>();

    /** Adds a policy to notify when camera is opened and closed. */
    public void addCameraStatePolicy(@NonNull AppCompatCameraStatePolicy policy) {
        mCameraStatePolicies.add(policy);
    }

    /** Removes a policy to notify about camera opened/closed signals. */
    public void removeCameraStatePolicy(@NonNull AppCompatCameraStatePolicy policy) {
        mCameraStatePolicies.remove(policy);
    }

    @Override
    public void onCameraOpened(@NonNull ActivityRecord cameraActivity) {
        for (int i = 0; i < mCameraStatePolicies.size(); i++) {
            mCameraStatePolicies.get(i).onCameraOpened(cameraActivity);
        }
    }

    /**
     * @return {@code false} if any listener has reported that they cannot process camera close now.
     */
    @Override
    public boolean canCameraBeClosed(@NonNull String cameraId) {
        for (int i = 0; i < mCameraStatePolicies.size(); i++) {
            if (!mCameraStatePolicies.get(i).canCameraBeClosed(cameraId)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void onCameraClosed() {
        for (int i = 0; i < mCameraStatePolicies.size(); i++) {
            mCameraStatePolicies.get(i).onCameraClosed();
        }
    }
}
+7 −3
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ import com.android.internal.protolog.WmProtoLogGroups;
 * changes to the camera and display orientation signals to match those expected on a portrait
 * device in that orientation (for example, on a standard phone).
 */
final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompatStateListener,
final class CameraCompatFreeformPolicy implements AppCompatCameraStatePolicy,
        ActivityRefresher.Evaluator {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "CameraCompatFreeformPolicy" : TAG_WM;

@@ -67,6 +67,8 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa
    @NonNull
    private final ActivityRefresher mActivityRefresher;
    @NonNull
    private final AppCompatCameraStateSource mCameraStateNotifier;
    @NonNull
    private final CameraStateMonitor mCameraStateMonitor;

    // TODO(b/380840084): Consider moving this to the CameraStateMonitor, and keeping track of
@@ -82,14 +84,16 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa

    CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent,
            @NonNull CameraStateMonitor cameraStateMonitor,
            @NonNull AppCompatCameraStateSource cameraStateNotifier,
            @NonNull ActivityRefresher activityRefresher) {
        mDisplayContent = displayContent;
        mCameraStateMonitor = cameraStateMonitor;
        mCameraStateNotifier = cameraStateNotifier;
        mActivityRefresher = activityRefresher;
    }

    void start() {
        mCameraStateMonitor.addCameraStateListener(this);
        mCameraStateNotifier.addCameraStatePolicy(this);
        mActivityRefresher.addEvaluator(this);
        mIsRunning = true;
    }
@@ -101,7 +105,7 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa

    /** Releases camera callback listener. */
    void dispose() {
        mCameraStateMonitor.removeCameraStateListener(this);
        mCameraStateNotifier.removeCameraStatePolicy(this);
        mActivityRefresher.removeEvaluator(this);
        mIsRunning = false;
    }
+21 −65
Original line number Diff line number Diff line
@@ -76,13 +76,14 @@ class CameraStateMonitor {
    // TODO(b/336474959): should/can this go in the compat listeners?
    private final Set<String> mScheduledCompatModeUpdateCameraIdSet = new ArraySet<>();

    private final ArrayList<CameraCompatStateListener> mCameraStateListeners = new ArrayList<>();
    @VisibleForTesting
    final AppCompatCameraStatePolicy mAppCompatCameraStatePolicy;

    /**
     * Value toggled on {@link #startListeningToCameraState()} to {@code true} and on {@link
     * #dispose()} to {@code false}.
     * #stopListeningToCameraState()} to {@code false}.
     */
    private boolean mIsRunning;
    private boolean mIsListeningToCameraState;

    private final CameraManager.AvailabilityCallback mAvailabilityCallback =
            new  CameraManager.AvailabilityCallback() {
@@ -100,42 +101,41 @@ class CameraStateMonitor {
                }
            };

    CameraStateMonitor(@NonNull DisplayContent displayContent, @NonNull Handler handler) {
    CameraStateMonitor(@NonNull DisplayContent displayContent, @NonNull Handler handler,
            @NonNull AppCompatCameraStatePolicy appCompatCameraStatePolicy) {
        // This constructor is called from DisplayContent constructor. Don't use any fields in
        // DisplayContent here since they aren't guaranteed to be set.
        mHandler = handler;
        mDisplayContent = displayContent;
        mAppCompatCameraStatePolicy = appCompatCameraStatePolicy;
        mWmService = displayContent.mWmService;
        mCameraManager = mWmService.mContext.getSystemService(CameraManager.class);
    }

    /** Starts listening to camera opened/closed signals. */
    void startListeningToCameraState() {
        if (mCameraManager != null) {
            mCameraManager.registerAvailabilityCallback(
                    mWmService.mContext.getMainExecutor(), mAvailabilityCallback);
        }
        mIsRunning = true;
        mIsListeningToCameraState = true;
    }

    /** Releases camera callback listener. */
    void dispose() {
    /** Stops listening to camera opened/closed signals. */
    public void stopListeningToCameraState() {
        if (mCameraManager != null) {
            mCameraManager.unregisterAvailabilityCallback(mAvailabilityCallback);
        }
        mIsRunning = false;
        mIsListeningToCameraState = false;
    }

    /**
     * Returns whether {@link CameraStateMonitor} is listening to camera opened/closed
     * signals.
     */
    @VisibleForTesting
    boolean isRunning() {
        return mIsRunning;
    }

    void addCameraStateListener(CameraCompatStateListener listener) {
        mCameraStateListeners.add(listener);
    }

    void removeCameraStateListener(CameraCompatStateListener listener) {
        mCameraStateListeners.remove(listener);
    boolean isListeningToCameraState() {
        return mIsListeningToCameraState;
    }

    private void notifyCameraOpenedWithDelay(@NonNull String cameraId,
@@ -169,14 +169,7 @@ class CameraStateMonitor {
            if (cameraActivity == null || cameraActivity.getTask() == null) {
                return;
            }
            notifyListenersCameraOpened(cameraActivity);
        }
    }

    private void notifyListenersCameraOpened(@NonNull ActivityRecord cameraActivity) {
        for (int i = 0; i < mCameraStateListeners.size(); i++) {
            CameraCompatStateListener listener = mCameraStateListeners.get(i);
            listener.onCameraOpened(cameraActivity);
            mAppCompatCameraStatePolicy.onCameraOpened(cameraActivity);
        }
    }

@@ -229,11 +222,11 @@ class CameraStateMonitor {
                // Already reconnected to this camera, no need to clean up.
                return;
            }
            final boolean canClose = checkCanCloseForAllListeners(cameraId);
            final boolean canClose = mAppCompatCameraStatePolicy.canCameraBeClosed(cameraId);
            if (canClose) {
                // Finish cleaning up.
                mCameraIdPackageBiMapping.removeCameraId(cameraId);
                notifyListenersCameraClosed();
                mAppCompatCameraStatePolicy.onCameraClosed();
            } else {
                // Not ready to process closure yet - the camera activity might be refreshing.
                // Try again later.
@@ -242,24 +235,6 @@ class CameraStateMonitor {
        }
    }

    /**
     * @return {@code false} if any listener has reported that they cannot process camera close now.
     */
    private boolean checkCanCloseForAllListeners(@NonNull String cameraId) {
        for (int i = 0; i < mCameraStateListeners.size(); i++) {
            if (!mCameraStateListeners.get(i).canCameraBeClosed(cameraId)) {
                return false;
            }
        }
        return true;
    }

    private void notifyListenersCameraClosed() {
        for (int i = 0; i < mCameraStateListeners.size(); i++) {
            mCameraStateListeners.get(i).onCameraClosed();
        }
    }

    // TODO(b/335165310): verify that this works in multi instance and permission dialogs.
    /**
     * Finds a visible activity with the given package name.
@@ -303,23 +278,4 @@ class CameraStateMonitor {
                + mCameraIdPackageBiMapping
                .getSummaryForDisplayRotationHistoryRecord();
    }

    interface CameraCompatStateListener {
        /**
         * Notifies the compat listener that an activity has opened camera.
         */
        void onCameraOpened(@NonNull ActivityRecord cameraActivity);
        /**
         * Checks whether a listener is ready to do a cleanup when camera is closed.
         *
         * <p>The notifier might try again if false is returned.
         */
        // TODO(b/336474959): try to decouple `cameraId` from the listeners.
        boolean canCameraBeClosed(@NonNull String cameraId);

        /**
         * Notifies the compat listener that camera is closed.
         */
        void onCameraClosed();
    }
}
Loading