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

Commit 7661b8bf authored by Darryl Johnson's avatar Darryl Johnson Committed by Automerger Merge Worker
Browse files

Merge changes I9ccdbd8c,Ifc24ba37 am: e4cbd037

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12701585

Change-Id: I849b3a7edb5a620eb851ce6a4bd40614df55eb23
parents a68dd515 e4cbd037
Loading
Loading
Loading
Loading
+45 −1
Original line number Diff line number Diff line
@@ -16,9 +16,12 @@

package android.hardware.devicestate;

import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;

import java.util.concurrent.Executor;

/**
 * Manages the state of the system for devices with user-configurable hardware like a foldable
 * phone.
@@ -33,6 +36,47 @@ public final class DeviceStateManager {
    private DeviceStateManagerGlobal mGlobal;

    public DeviceStateManager() {
        mGlobal = DeviceStateManagerGlobal.getInstance();
        DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
        if (global == null) {
            throw new IllegalStateException(
                    "Failed to get instance of global device state manager.");
        }
        mGlobal = global;
    }

    /**
     * Registers a listener to receive notifications about changes in device state.
     *
     * @param listener the listener to register.
     * @param executor the executor to process notifications.
     *
     * @see DeviceStateListener
     */
    public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
            @NonNull Executor executor) {
        mGlobal.registerDeviceStateListener(listener, executor);
    }

    /**
     * Unregisters a listener previously registered with
     * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
     */
    public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
        mGlobal.unregisterDeviceStateListener(listener);
    }

    /**
     * Listens for changes in device states.
     */
    public interface DeviceStateListener {
        /**
         * Called in response to device state changes.
         * <p>
         * Guaranteed to be called once on registration of the listener with the
         * initial value and then on every subsequent change in device state.
         *
         * @param deviceState the new device state.
         */
        void onDeviceStateChanged(int deviceState);
    }
}
+123 −2
Original line number Diff line number Diff line
@@ -19,16 +19,26 @@ package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;

import java.util.ArrayList;
import java.util.concurrent.Executor;

/**
 * Provides communication with the device state system service on behalf of applications.
 *
 * @see DeviceStateManager
 * @hide
 */
final class DeviceStateManagerGlobal {
@VisibleForTesting(visibility = Visibility.PACKAGE)
public final class DeviceStateManagerGlobal {
    private static DeviceStateManagerGlobal sInstance;

    /**
@@ -49,10 +59,121 @@ final class DeviceStateManagerGlobal {
        }
    }

    private final Object mLock = new Object();
    @NonNull
    private final IDeviceStateManager mDeviceStateManager;
    @Nullable
    private DeviceStateManagerCallback mCallback;

    private DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
    @GuardedBy("mLock")
    private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
    @Nullable
    @GuardedBy("mLock")
    private Integer mLastReceivedState;

    @VisibleForTesting
    public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
        mDeviceStateManager = deviceStateManager;
    }

    /**
     * Registers a listener to receive notifications about changes in device state.
     *
     * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
     */
    @VisibleForTesting(visibility = Visibility.PACKAGE)
    public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
            @NonNull Executor executor) {
        Integer stateToReport;
        DeviceStateListenerWrapper wrapper;
        synchronized (mLock) {
            registerCallbackIfNeededLocked();

            int index = findListenerLocked(listener);
            if (index != -1) {
                // This listener is already registered.
                return;
            }

            wrapper = new DeviceStateListenerWrapper(listener, executor);
            mListeners.add(wrapper);
            stateToReport = mLastReceivedState;
        }

        if (stateToReport != null) {
            // Notify the listener with the most recent device state from the server. If the state
            // to report is null this is likely the first listener added and we're still waiting
            // from the callback from the server.
            wrapper.notifyDeviceStateChanged(stateToReport);
        }
    }

    /**
     * Unregisters a listener previously registered with
     * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
     *
     * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
     */
    @VisibleForTesting(visibility = Visibility.PACKAGE)
    public void unregisterDeviceStateListener(DeviceStateListener listener) {
        synchronized (mLock) {
            int indexToRemove = findListenerLocked(listener);
            if (indexToRemove != -1) {
                mListeners.remove(indexToRemove);
            }
        }
    }

    private void registerCallbackIfNeededLocked() {
        if (mCallback == null) {
            mCallback = new DeviceStateManagerCallback();
            try {
                mDeviceStateManager.registerCallback(mCallback);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

    private int findListenerLocked(DeviceStateListener listener) {
        for (int i = 0; i < mListeners.size(); i++) {
            if (mListeners.get(i).mDeviceStateListener.equals(listener)) {
                return i;
            }
        }
        return -1;
    }

    private void handleDeviceStateChanged(int newDeviceState) {
        ArrayList<DeviceStateListenerWrapper> listeners;
        synchronized (mLock) {
            mLastReceivedState = newDeviceState;
            listeners = new ArrayList<>(mListeners);
        }

        for (int i = 0; i < listeners.size(); i++) {
            listeners.get(i).notifyDeviceStateChanged(newDeviceState);
        }
    }

    private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
        @Override
        public void onDeviceStateChanged(int deviceState) {
            handleDeviceStateChanged(deviceState);
        }
    }

    private static final class DeviceStateListenerWrapper {
        private final DeviceStateListener mDeviceStateListener;
        private final Executor mExecutor;

        DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) {
            mDeviceStateListener = listener;
            mExecutor = executor;
        }

        void notifyDeviceStateChanged(int newDeviceState) {
            mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
        }
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -16,5 +16,9 @@

package android.hardware.devicestate;

import android.hardware.devicestate.IDeviceStateManagerCallback;

/** @hide */
interface IDeviceStateManager {}
interface IDeviceStateManager {
    void registerCallback(in IDeviceStateManagerCallback callback);
}
+22 −0
Original line number Diff line number Diff line
/**
 * Copyright (c) 2020, 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.hardware.devicestate;

/** @hide */
interface IDeviceStateManagerCallback {
    oneway void onDeviceStateChanged(int deviceState);
}
+26 −0
Original line number Diff line number Diff line
// Copyright (C) 2020 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.

android_test {
    name: "FrameworksCoreDeviceStateManagerTests",
    // Include all test java files
    srcs: ["src/**/*.java"],
    static_libs: [
        "androidx.test.rules",
        "frameworks-base-testutils",
    ],
    libs: ["android.test.runner"],
    platform_apis: true,
    certificate: "platform",
}
Loading