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

Commit 98411c1e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Checks that state requested from top app is a state we allow" into tm-dev am: 67580353

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



Change-Id: I787e24842cc473a6b71fffb7c04ed29e355e888e
Ignore-AOSP-First: this is an automerge
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents b6b823de 67580353
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -5778,4 +5778,7 @@
    <string name="safety_protection_display_text"></string>

    <!-- End safety protection resources to be overlaid -->

    <!-- List of the labels of requestable device state config values -->
    <string-array name="config_deviceStatesAvailableForAppRequests"/>
</resources>
+1 −0
Original line number Diff line number Diff line
@@ -4774,6 +4774,7 @@
  <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" />
  <java-symbol type="drawable" name="ic_swap_horiz" />
  <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" />
  <java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" />

  <!-- For app language picker -->
  <java-symbol type="string" name="system_locale_title" />
+74 −8
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.os.ShellCallback;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -59,7 +60,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;

/**
@@ -139,6 +142,8 @@ public final class DeviceStateManagerService extends SystemService {
    @GuardedBy("mLock")
    private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();

    private Set<Integer> mDeviceStatesAvailableForAppRequests;

    public DeviceStateManagerService(@NonNull Context context) {
        this(context, DeviceStatePolicy.Provider
                .fromResources(context.getResources())
@@ -164,6 +169,10 @@ public final class DeviceStateManagerService extends SystemService {
    public void onStart() {
        publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
        publishLocalService(DeviceStateManagerInternal.class, new LocalService());

        synchronized (mLock) {
            readStatesAvailableForRequestFromApps();
        }
    }

    @VisibleForTesting
@@ -626,21 +635,78 @@ public final class DeviceStateManagerService extends SystemService {

    /**
     * Allow top processes to request or cancel a device state change. If the calling process ID is
     * not the top app, then check if this process holds the
     * {@link android.Manifest.permission.CONTROL_DEVICE_STATE} permission. If the calling process
     * is the top app, check to verify they are requesting a state we've deemed to be able to be
     * available for an app process to request. States that can be requested are based around
     * features that we've created that require specific device state overrides.
     * @param callingPid Process ID that is requesting this state change
     * @param state state that is being requested.
     */
    private void assertCanRequestDeviceState(int callingPid, int state) {
        final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
        if (topApp == null || topApp.getPid() != callingPid
                || !isStateAvailableForAppRequests(state)) {
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to request device state, "
                            + "or the call must come from the top app "
                            + "and be a device state that is available for apps to request.");
        }
    }

    /**
     * Checks if the process can control the device state. If the calling process ID is
     * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
     * @param callingPid
     *
     * @param callingPid Process ID that is requesting this state change
     */
    private void checkCanControlDeviceState(int callingPid) {
        // Allow top processes to request a device state change
        // If the calling process ID is not the top app, then we check if this process
        // holds a permission to CONTROL_DEVICE_STATE
    private void assertCanControlDeviceState(int callingPid) {
        final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
        if (topApp == null || topApp.getPid() != callingPid) {
            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                    "Permission required to request device state, "
                            + "or the call must come from the top focused app.");
                            + "or the call must come from the top app.");
        }
    }

    private boolean isStateAvailableForAppRequests(int state) {
        synchronized (mLock) {
            return mDeviceStatesAvailableForAppRequests.contains(state);
        }
    }

    /**
     * Adds device state values that are available to be requested by the top level app.
     */
    @GuardedBy("mLock")
    private void readStatesAvailableForRequestFromApps() {
        mDeviceStatesAvailableForAppRequests = new HashSet<>();
        String[] availableAppStatesConfigIdentifiers = getContext().getResources()
                .getStringArray(R.array.config_deviceStatesAvailableForAppRequests);
        for (int i = 0; i < availableAppStatesConfigIdentifiers.length; i++) {
            String identifierToFetch = availableAppStatesConfigIdentifiers[i];
            int configValueIdentifier = getContext().getResources()
                    .getIdentifier(identifierToFetch, "integer", "android");
            int state = getContext().getResources().getInteger(configValueIdentifier);
            if (isValidState(state)) {
                mDeviceStatesAvailableForAppRequests.add(state);
            } else {
                Slog.e(TAG, "Invalid device state was found in the configuration file. State id: "
                        + state);
            }
        }
    }

    @GuardedBy("mLock")
    private boolean isValidState(int state) {
        for (int i = 0; i < mDeviceStates.size(); i++) {
            if (state == mDeviceStates.valueAt(i).getIdentifier()) {
                return true;
            }
        }
        return false;
    }

    private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
        @Override
        public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
@@ -777,7 +843,7 @@ public final class DeviceStateManagerService extends SystemService {
            // Allow top processes to request a device state change
            // If the calling process ID is not the top app, then we check if this process
            // holds a permission to CONTROL_DEVICE_STATE
            checkCanControlDeviceState(callingPid);
            assertCanRequestDeviceState(callingPid, state);

            if (token == null) {
                throw new IllegalArgumentException("Request token must not be null.");
@@ -797,7 +863,7 @@ public final class DeviceStateManagerService extends SystemService {
            // Allow top processes to cancel a device state change
            // If the calling process ID is not the top app, then we check if this process
            // holds a permission to CONTROL_DEVICE_STATE
            checkCanControlDeviceState(callingPid);
            assertCanControlDeviceState(callingPid);

            final long callingIdentity = Binder.clearCallingIdentity();
            try {