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

Commit b92ff23b authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Rename MonitoredDeviceState to DeviceMonitor

Also:
- Don't make InputDevicesChangedListener part of the BatteryController
  interface, since it's not used for testing.
- Report a copy of the battery state when notifying components outside
  of BatteryController, becuase reporting a member object would
  leak the internal state to other components.
- Explicitly mark all interface methods as public.

Bug: 243005009
Test: atest FrameworkServicesTests
Test: atest FrameworkCoreTests

Change-Id: Iefa08f4f1b52b8dd48dc273e90e90c92d15d4ed4
parent 8605f83e
Loading
Loading
Loading
Loading
+90 −82
Original line number Diff line number Diff line
@@ -48,8 +48,13 @@ import java.util.Set;
/**
 * A thread-safe component of {@link InputManagerService} responsible for managing the battery state
 * of input devices.
 *
 * Interactions with BatteryController can happen on several threads, including Binder threads, the
 * {@link UEventObserver}'s thread, or its own Handler thread, among others. All public methods, and
 * private methods prefixed with "handle-" (e.g. {@link #handleListeningProcessDied(int)}),
 * serve as entry points for these threads.
 */
final class BatteryController implements InputManager.InputDeviceListener {
final class BatteryController {
    private static final String TAG = BatteryController.class.getSimpleName();

    // To enable these logs, run:
@@ -70,10 +75,9 @@ final class BatteryController implements InputManager.InputDeviceListener {
    @GuardedBy("mLock")
    private final ArrayMap<Integer, ListenerRecord> mListenerRecords = new ArrayMap<>();

    // Maps a deviceId that is being monitored to the battery state for the device.
    // This must be kept in sync with {@link #mListenerRecords}.
    // Maps a deviceId that is being monitored to the monitor for the battery state of the device.
    @GuardedBy("mLock")
    private final ArrayMap<Integer, MonitoredDeviceState> mMonitoredDeviceStates = new ArrayMap<>();
    private final ArrayMap<Integer, DeviceMonitor> mDeviceMonitors = new ArrayMap<>();

    @GuardedBy("mLock")
    private boolean mIsPolling = false;
@@ -93,9 +97,9 @@ final class BatteryController implements InputManager.InputDeviceListener {
        mUEventManager = uEventManager;
    }

    void systemRunning() {
    public void systemRunning() {
        Objects.requireNonNull(mContext.getSystemService(InputManager.class))
                .registerInputDeviceListener(this, mHandler);
                .registerInputDeviceListener(mInputDeviceListener, mHandler);
    }

    /**
@@ -103,7 +107,7 @@ final class BatteryController implements InputManager.InputDeviceListener {
     * state.
     */
    @BinderThread
    void registerBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
    public void registerBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
            int pid) {
        synchronized (mLock) {
            ListenerRecord listenerRecord = mListenerRecords.get(pid);
@@ -132,11 +136,11 @@ final class BatteryController implements InputManager.InputDeviceListener {
                                + " is already monitoring deviceId " + deviceId);
            }

            MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
            if (deviceState == null) {
            DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
            if (monitor == null) {
                // This is the first listener that is monitoring this device.
                deviceState = new MonitoredDeviceState(deviceId);
                mMonitoredDeviceStates.put(deviceId, deviceState);
                monitor = new DeviceMonitor(deviceId);
                mDeviceMonitors.put(deviceId, monitor);
            }

            if (DEBUG) {
@@ -145,37 +149,35 @@ final class BatteryController implements InputManager.InputDeviceListener {
            }

            updatePollingLocked(true /*delayStart*/);
            notifyBatteryListener(listenerRecord, deviceState);
            notifyBatteryListener(listenerRecord, monitor.getBatteryStateForReporting());
        }
    }

    private static void notifyBatteryListener(ListenerRecord listenerRecord,
            MonitoredDeviceState deviceState) {
    private static void notifyBatteryListener(ListenerRecord listenerRecord, State state) {
        try {
            listenerRecord.mListener.onBatteryStateChanged(
                    new State(deviceState.mState));
            listenerRecord.mListener.onBatteryStateChanged(state);
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to notify listener", e);
        }
        if (DEBUG) {
            Slog.d(TAG, "Notified battery listener from pid " + listenerRecord.mPid
                    + " of state of deviceId " + deviceState.mState.deviceId);
                    + " of state of deviceId " + state.deviceId);
        }
    }

    @GuardedBy("mLock")
    private void notifyAllListenersForDeviceLocked(MonitoredDeviceState deviceState) {
        if (DEBUG) Slog.d(TAG, "Notifying all listeners of battery state: " + deviceState);
    private void notifyAllListenersForDeviceLocked(State state) {
        if (DEBUG) Slog.d(TAG, "Notifying all listeners of battery state: " + state);
        mListenerRecords.forEach((pid, listenerRecord) -> {
            if (listenerRecord.mMonitoredDevices.contains(deviceState.mState.deviceId)) {
                notifyBatteryListener(listenerRecord, deviceState);
            if (listenerRecord.mMonitoredDevices.contains(state.deviceId)) {
                notifyBatteryListener(listenerRecord, state);
            }
        });
    }

    @GuardedBy("mLock")
    private void updatePollingLocked(boolean delayStart) {
        if (mMonitoredDeviceStates.isEmpty() || !mIsInteractive) {
        if (mDeviceMonitors.isEmpty() || !mIsInteractive) {
            // Stop polling.
            mIsPolling = false;
            mHandler.removeCallbacks(this::handlePollEvent);
@@ -198,8 +200,8 @@ final class BatteryController implements InputManager.InputDeviceListener {
    }

    @GuardedBy("mLock")
    private MonitoredDeviceState getDeviceStateOrThrowLocked(int deviceId) {
        return Objects.requireNonNull(mMonitoredDeviceStates.get(deviceId),
    private DeviceMonitor getDeviceMonitorOrThrowLocked(int deviceId) {
        return Objects.requireNonNull(mDeviceMonitors.get(deviceId),
                "Maps are out of sync: Cannot find device state for deviceId " + deviceId);
    }

@@ -209,8 +211,8 @@ final class BatteryController implements InputManager.InputDeviceListener {
     * removed.
     */
    @BinderThread
    void unregisterBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
            int pid) {
    public void unregisterBatteryListener(int deviceId,
            @NonNull IInputDeviceBatteryListener listener, int pid) {
        synchronized (mLock) {
            final ListenerRecord listenerRecord = mListenerRecords.get(pid);
            if (listenerRecord == null) {
@@ -249,9 +251,9 @@ final class BatteryController implements InputManager.InputDeviceListener {

        if (!hasRegisteredListenerForDeviceLocked(deviceId)) {
            // There are no more listeners monitoring this device.
            final MonitoredDeviceState deviceState = getDeviceStateOrThrowLocked(deviceId);
            deviceState.stopMonitoring();
            mMonitoredDeviceStates.remove(deviceId);
            final DeviceMonitor monitor = getDeviceMonitorOrThrowLocked(deviceId);
            monitor.stopMonitoring();
            mDeviceMonitors.remove(deviceId);
        }

        if (listenerRecord.mMonitoredDevices.isEmpty()) {
@@ -290,15 +292,14 @@ final class BatteryController implements InputManager.InputDeviceListener {
        }
    }

    // Query the battery state for the device and notify all listeners if there is a change.
    private void handleBatteryChangeNotification(int deviceId, long eventTime) {
    private void handleUEventNotification(int deviceId, long eventTime) {
        synchronized (mLock) {
            final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
            if (deviceState == null) {
            final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
            if (monitor == null) {
                return;
            }
            if (deviceState.updateBatteryState(eventTime)) {
                notifyAllListenersForDeviceLocked(deviceState);
            if (monitor.updateBatteryState(eventTime)) {
                notifyAllListenersForDeviceLocked(monitor.getBatteryStateForReporting());
            }
        }
    }
@@ -309,11 +310,11 @@ final class BatteryController implements InputManager.InputDeviceListener {
                return;
            }
            final long eventTime = SystemClock.uptimeMillis();
            mMonitoredDeviceStates.forEach((deviceId, deviceState) -> {
            mDeviceMonitors.forEach((deviceId, monitor) -> {
                // Re-acquire lock in the lambda to silence error-prone build warnings.
                synchronized (mLock) {
                    if (deviceState.updateBatteryState(eventTime)) {
                        notifyAllListenersForDeviceLocked(deviceState);
                    if (monitor.updateBatteryState(eventTime)) {
                        notifyAllListenersForDeviceLocked(monitor.getBatteryStateForReporting());
                    }
                }
            });
@@ -322,31 +323,32 @@ final class BatteryController implements InputManager.InputDeviceListener {
    }

    /** Gets the current battery state of an input device. */
    IInputDeviceBatteryState getBatteryState(int deviceId) {
    public IInputDeviceBatteryState getBatteryState(int deviceId) {
        synchronized (mLock) {
            final long updateTime = SystemClock.uptimeMillis();
            final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
            if (deviceState == null) {
            final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
            if (monitor == null) {
                // The input device's battery is not being monitored by any listener.
                return queryBatteryStateFromNative(deviceId, updateTime);
            } else {
                // Force the battery state to update, and notify listeners if necessary.
                if (deviceState.updateBatteryState(updateTime)) {
                    notifyAllListenersForDeviceLocked(deviceState);
            }
                return new State(deviceState.mState);
            // Force the battery state to update, and notify listeners if necessary.
            final boolean stateChanged = monitor.updateBatteryState(updateTime);
            final State state = monitor.getBatteryStateForReporting();
            if (stateChanged) {
                notifyAllListenersForDeviceLocked(state);
            }
            return state;
        }
    }

    void onInteractiveChanged(boolean interactive) {
    public void onInteractiveChanged(boolean interactive) {
        synchronized (mLock) {
            mIsInteractive = interactive;
            updatePollingLocked(false /*delayStart*/);
        }
    }

    void dump(PrintWriter pw, String prefix) {
    public void dump(PrintWriter pw, String prefix) {
        synchronized (mLock) {
            final String indent = prefix + "  ";
            final String indent2 = indent + "  ";
@@ -360,50 +362,50 @@ final class BatteryController implements InputManager.InputDeviceListener {
                pw.println(indent2 + i + ": " + mListenerRecords.valueAt(i));
            }

            pw.println(indent + "Monitored devices: " + mMonitoredDeviceStates.size() + " devices");
            for (int i = 0; i < mMonitoredDeviceStates.size(); i++) {
                pw.println(indent2 + i + ": " + mMonitoredDeviceStates.valueAt(i));
            pw.println(indent + "Device Monitors: " + mDeviceMonitors.size() + " monitors");
            for (int i = 0; i < mDeviceMonitors.size(); i++) {
                pw.println(indent2 + i + ": " + mDeviceMonitors.valueAt(i));
            }
        }
    }

    @SuppressWarnings("all")
    void monitor() {
    public void monitor() {
        synchronized (mLock) {
            return;
        }
    }

    @VisibleForTesting
    private final InputManager.InputDeviceListener mInputDeviceListener =
            new InputManager.InputDeviceListener() {
        @Override
        public void onInputDeviceAdded(int deviceId) {}

    @VisibleForTesting
        @Override
        public void onInputDeviceRemoved(int deviceId) {}

    @VisibleForTesting
        @Override
        public void onInputDeviceChanged(int deviceId) {
            synchronized (mLock) {
            final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
            if (deviceState == null) {
                final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
                if (monitor == null) {
                    return;
                }
                final long eventTime = SystemClock.uptimeMillis();
            if (deviceState.updateBatteryState(eventTime)) {
                notifyAllListenersForDeviceLocked(deviceState);
                if (monitor.updateBatteryState(eventTime)) {
                    notifyAllListenersForDeviceLocked(monitor.getBatteryStateForReporting());
                }
            }
        }
    };

    // A record of a registered battery listener from one process.
    private class ListenerRecord {
        final int mPid;
        final IInputDeviceBatteryListener mListener;
        final IBinder.DeathRecipient mDeathRecipient;
        public final int mPid;
        public final IInputDeviceBatteryListener mListener;
        public final IBinder.DeathRecipient mDeathRecipient;
        // The set of deviceIds that are currently being monitored by this listener.
        final Set<Integer> mMonitoredDevices;
        public final Set<Integer> mMonitoredDevices;

        ListenerRecord(int pid, IInputDeviceBatteryListener listener) {
            mPid = pid;
@@ -431,14 +433,14 @@ final class BatteryController implements InputManager.InputDeviceListener {
    }

    // Holds the state of an InputDevice for which battery changes are currently being monitored.
    private class MonitoredDeviceState {
    private class DeviceMonitor {
        @NonNull
        private State mState;

        @Nullable
        private UEventListener mUEventListener;

        MonitoredDeviceState(int deviceId) {
        DeviceMonitor(int deviceId) {
            mState = new State(deviceId);

            // Load the initial battery state and start monitoring.
@@ -447,7 +449,7 @@ final class BatteryController implements InputManager.InputDeviceListener {
        }

        // Returns true if the battery state changed since the last time it was updated.
        boolean updateBatteryState(long updateTime) {
        public boolean updateBatteryState(long updateTime) {
            mState.updateTime = updateTime;

            final State updatedState = queryBatteryStateFromNative(mState.deviceId, updateTime);
@@ -470,23 +472,29 @@ final class BatteryController implements InputManager.InputDeviceListener {
            if (batteryPath == null) {
                return;
            }
            final int deviceId = mState.deviceId;
            mUEventListener = new UEventListener() {
                @Override
                void onUEvent(long eventTime) {
                    handleBatteryChangeNotification(mState.deviceId, eventTime);
                public void onUEvent(long eventTime) {
                    handleUEventNotification(deviceId, eventTime);
                }
            };
            mUEventManager.addListener(mUEventListener, "DEVPATH=" + batteryPath);
        }

        // This must be called when the device is no longer being monitored.
        void stopMonitoring() {
        public void stopMonitoring() {
            if (mUEventListener != null) {
                mUEventManager.removeListener(mUEventListener);
                mUEventListener = null;
            }
        }

        // Returns the current battery state that can be used to notify listeners BatteryController.
        public State getBatteryStateForReporting() {
            return new State(mState);
        }

        @Override
        public String toString() {
            return "state=" + mState
@@ -513,7 +521,7 @@ final class BatteryController implements InputManager.InputDeviceListener {
                }
            };

            abstract void onUEvent(long eventTime);
            public abstract void onUEvent(long eventTime);
        }

        default void addListener(UEventListener listener, String match) {