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

Commit 4526eddd authored by Darryl L Johnson's avatar Darryl L Johnson
Browse files

Add logic to forward foldable device state to native cameraserver.

This change adds two methods to CameraServiceProxy to be used within
system_server for setting camera device state flags. It also updates
DisplayFoldController to leverage these methods.

Bug: 153788656
Bug: 154038218
Bug: 159401801
Test: Trigger fold and verify camera's toggle
Change-Id: I08392a62bc65d10a0715ef0d831c8bb919035a5d
parent 323c19b7
Loading
Loading
Loading
Loading
+126 −14
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.server.camera;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -39,6 +40,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
@@ -48,6 +50,8 @@ import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.android.server.wm.WindowManagerInternal;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -79,10 +83,19 @@ public class CameraServiceProxy extends SystemService

    // Handler message codes
    private static final int MSG_SWITCH_USER = 1;
    private static final int MSG_NOTIFY_DEVICE_STATE = 2;

    private static final int RETRY_DELAY_TIME = 20; //ms
    private static final int RETRY_TIMES = 60;

    @IntDef(flag = true, prefix = { "DEVICE_STATE_" }, value = {
            ICameraService.DEVICE_STATE_BACK_COVERED,
            ICameraService.DEVICE_STATE_FRONT_COVERED,
            ICameraService.DEVICE_STATE_FOLDED
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface DeviceStateFlags {}

    // Maximum entries to keep in usage history before dumping out
    private static final int MAX_USAGE_HISTORY = 100;

@@ -94,6 +107,15 @@ public class CameraServiceProxy extends SystemService
    private final Object mLock = new Object();
    private Set<Integer> mEnabledCameraUsers;
    private int mLastUser;
    // The current set of device state flags. May be different from mLastReportedDeviceState if the
    // native camera service has not been notified of the change.
    @GuardedBy("mLock")
    @DeviceStateFlags
    private int mDeviceState;
    // The most recent device state flags reported to the native camera server.
    @GuardedBy("mLock")
    @DeviceStateFlags
    private int mLastReportedDeviceState;

    private ICameraService mCameraServiceRaw;

@@ -185,6 +207,7 @@ public class CameraServiceProxy extends SystemService
                return;
            }
            notifySwitchWithRetries(RETRY_TIMES);
            notifyDeviceStateWithRetries(RETRY_TIMES);
        }

        @Override
@@ -218,12 +241,55 @@ public class CameraServiceProxy extends SystemService
        mLogWriterService.allowCoreThreadTimeOut(true);
    }

    /**
     * Sets the device state bits set within {@code deviceStateFlags} leaving all other bits the
     * same.
     * <p>
     * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}.
     *
     * @param deviceStateFlags a bitmask of the device state bits that should be set.
     *
     * @see #clearDeviceStateFlags(int)
     */
    public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
        synchronized (mLock) {
            mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
            mDeviceState |= deviceStateFlags;
            if (mDeviceState != mLastReportedDeviceState) {
                notifyDeviceStateWithRetriesLocked(RETRY_TIMES);
            }
        }
    }

    /**
     * Clears the device state bits set within {@code deviceStateFlags} leaving all other bits the
     * same.
     * <p>
     * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}.
     *
     * @param deviceStateFlags a bitmask of the device state bits that should be cleared.
     *
     * @see #setDeviceStateFlags(int)
     */
    public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
        synchronized (mLock) {
            mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
            mDeviceState &= ~deviceStateFlags;
            if (mDeviceState != mLastReportedDeviceState) {
                notifyDeviceStateWithRetriesLocked(RETRY_TIMES);
            }
        }
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch(msg.what) {
            case MSG_SWITCH_USER: {
                notifySwitchWithRetries(msg.arg1);
            } break;
            case MSG_NOTIFY_DEVICE_STATE: {
                notifyDeviceStateWithRetries(msg.arg1);
            } break;
            default: {
                Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
            } break;
@@ -386,6 +452,25 @@ public class CameraServiceProxy extends SystemService
        }
    }

    @Nullable
    private ICameraService getCameraServiceRawLocked() {
        if (mCameraServiceRaw == null) {
            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
            if (cameraServiceBinder == null) {
                return null;
            }
            try {
                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
            } catch (RemoteException e) {
                Slog.w(TAG, "Could not link to death of native camera service");
                return null;
            }

            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
        }
        return mCameraServiceRaw;
    }

    private void switchUserLocked(int userHandle) {
        Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
        mLastUser = userHandle;
@@ -431,29 +516,56 @@ public class CameraServiceProxy extends SystemService
    private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) {
        // Forward the user switch event to the native camera service running in the cameraserver
        // process.
        if (mCameraServiceRaw == null) {
            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
            if (cameraServiceBinder == null) {
        ICameraService cameraService = getCameraServiceRawLocked();
        if (cameraService == null) {
            Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
                return false; // Camera service not active, cannot evict user clients.
            return false;
        }

        try {
                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
            mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
        } catch (RemoteException e) {
                Slog.w(TAG, "Could not link to death of native camera service");
            Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
            // Not much we can do if camera service is dead.
            return false;
        }
        return true;
    }

            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
    private void notifyDeviceStateWithRetries(int retries) {
        synchronized (mLock) {
            notifyDeviceStateWithRetriesLocked(retries);
        }
    }

    private void notifyDeviceStateWithRetriesLocked(int retries) {
        if (notifyDeviceStateChangeLocked(mDeviceState)) {
            return;
        }
        if (retries <= 0) {
            return;
        }
        Slog.i(TAG, "Could not notify camera service of device state change, retrying...");
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_NOTIFY_DEVICE_STATE, retries - 1,
                0, null), RETRY_DELAY_TIME);
    }

    private boolean notifyDeviceStateChangeLocked(@DeviceStateFlags int deviceState) {
        // Forward the state to the native camera service running in the cameraserver process.
        ICameraService cameraService = getCameraServiceRawLocked();
        if (cameraService == null) {
            Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
            return false;
        }

        try {
            mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
            mCameraServiceRaw.notifyDeviceStateChange(deviceState);
        } catch (RemoteException e) {
            Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
            // Not much we can do if camera service is dead.
            return false;
        }
        mLastReportedDeviceState = deviceState;
        return true;
    }

+27 −4
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.server.policy;

import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.ICameraService;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -26,11 +28,13 @@ import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDisplayFoldListener;

import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.camera.CameraServiceProxy;
import com.android.server.wm.WindowManagerInternal;

/**
@@ -38,11 +42,13 @@ import com.android.server.wm.WindowManagerInternal;
 * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
 */
class DisplayFoldController {

    private static final String TAG = "DisplayFoldController";

    private final WindowManagerInternal mWindowManagerInternal;
    private final DisplayManagerInternal mDisplayManagerInternal;
    // Camera service proxy can be disabled through a config.
    @Nullable
    private final CameraServiceProxy mCameraServiceProxy;
    private final int mDisplayId;
    private final Handler mHandler;

@@ -58,10 +64,12 @@ class DisplayFoldController {
    private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger();

    DisplayFoldController(WindowManagerInternal windowManagerInternal,
            DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
            DisplayManagerInternal displayManagerInternal,
            @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea,
            Handler handler) {
        mWindowManagerInternal = windowManagerInternal;
        mDisplayManagerInternal = displayManagerInternal;
        mCameraServiceProxy = cameraServiceProxy;
        mDisplayId = displayId;
        mFoldedArea = new Rect(foldedArea);
        mHandler = handler;
@@ -116,6 +124,16 @@ class DisplayFoldController {
            }
        }

        if (mCameraServiceProxy != null) {
            if (folded) {
                mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
            } else {
                mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
            }
        } else {
            Slog.w(TAG, "Camera service unavailable to toggle folded state.");
        }

        mDurationLogger.setDeviceFolded(folded);
        mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
        mFolded = folded;
@@ -193,8 +211,13 @@ class DisplayFoldController {
    }

    static DisplayFoldController create(Context context, int displayId) {
        final WindowManagerInternal windowManagerService =
                LocalServices.getService(WindowManagerInternal.class);
        final DisplayManagerInternal displayService =
                LocalServices.getService(DisplayManagerInternal.class);
        final CameraServiceProxy cameraServiceProxy =
                LocalServices.getService(CameraServiceProxy.class);

        final String configFoldedArea = context.getResources().getString(
                com.android.internal.R.string.config_foldedArea);
        final Rect foldedArea;
@@ -204,7 +227,7 @@ class DisplayFoldController {
            foldedArea = Rect.unflattenFromString(configFoldedArea);
        }

        return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class),
                displayService, displayId, foldedArea, DisplayThread.getHandler());
        return new DisplayFoldController(windowManagerService, displayService, cameraServiceProxy,
                displayId, foldedArea, DisplayThread.getHandler());
    }
}
+6 −6
Original line number Diff line number Diff line
@@ -1265,6 +1265,12 @@ public final class SystemServer implements Dumpable {
            inputManager = new InputManagerService(context);
            t.traceEnd();

            if (!disableCameraService) {
                t.traceBegin("StartCameraServiceProxy");
                mSystemServiceManager.startService(CameraServiceProxy.class);
                t.traceEnd();
            }

            t.traceBegin("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
@@ -2190,12 +2196,6 @@ public final class SystemServer implements Dumpable {
            t.traceEnd();
        }

        if (!disableCameraService) {
            t.traceBegin("StartCameraServiceProxy");
            mSystemServiceManager.startService(CameraServiceProxy.class);
            t.traceEnd();
        }

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
            t.traceBegin("StartIoTSystemService");
            mSystemServiceManager.startService(IOT_SERVICE_CLASS);