Loading core/res/res/values/config.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -5778,4 +5778,7 @@ <string name="safety_protection_display_text"></string> <string name="safety_protection_display_text"></string> <!-- End safety protection resources to be overlaid --> <!-- End safety protection resources to be overlaid --> <!-- List of the labels of requestable device state config values --> <string-array name="config_deviceStatesAvailableForAppRequests"/> </resources> </resources> core/res/res/values/symbols.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -4774,6 +4774,7 @@ <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" /> <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="drawable" name="ic_swap_horiz" /> <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> <java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" /> <!-- For app language picker --> <!-- For app language picker --> <java-symbol type="string" name="system_locale_title" /> <java-symbol type="string" name="system_locale_title" /> Loading services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +74 −8 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.os.ShellCallback; import android.util.Slog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils; Loading @@ -59,7 +60,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Optional; import java.util.Set; import java.util.WeakHashMap; import java.util.WeakHashMap; /** /** Loading Loading @@ -139,6 +142,8 @@ public final class DeviceStateManagerService extends SystemService { @GuardedBy("mLock") @GuardedBy("mLock") private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); private Set<Integer> mDeviceStatesAvailableForAppRequests; public DeviceStateManagerService(@NonNull Context context) { public DeviceStateManagerService(@NonNull Context context) { this(context, DeviceStatePolicy.Provider this(context, DeviceStatePolicy.Provider .fromResources(context.getResources()) .fromResources(context.getResources()) Loading @@ -164,6 +169,10 @@ public final class DeviceStateManagerService extends SystemService { public void onStart() { public void onStart() { publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); publishLocalService(DeviceStateManagerInternal.class, new LocalService()); publishLocalService(DeviceStateManagerInternal.class, new LocalService()); synchronized (mLock) { readStatesAvailableForRequestFromApps(); } } } @VisibleForTesting @VisibleForTesting Loading Loading @@ -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 * 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. * 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) { private void assertCanControlDeviceState(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 final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); if (topApp == null || topApp.getPid() != callingPid) { if (topApp == null || topApp.getPid() != callingPid) { getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, "Permission required to request 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 { private final class DeviceStateProviderListener implements DeviceStateProvider.Listener { @Override @Override public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) { public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) { Loading Loading @@ -777,7 +843,7 @@ public final class DeviceStateManagerService extends SystemService { // Allow top processes to request a device state change // 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 // If the calling process ID is not the top app, then we check if this process // holds a permission to CONTROL_DEVICE_STATE // holds a permission to CONTROL_DEVICE_STATE checkCanControlDeviceState(callingPid); assertCanRequestDeviceState(callingPid, state); if (token == null) { if (token == null) { throw new IllegalArgumentException("Request token must not be null."); throw new IllegalArgumentException("Request token must not be null."); Loading @@ -797,7 +863,7 @@ public final class DeviceStateManagerService extends SystemService { // Allow top processes to cancel a device state change // 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 // If the calling process ID is not the top app, then we check if this process // holds a permission to CONTROL_DEVICE_STATE // holds a permission to CONTROL_DEVICE_STATE checkCanControlDeviceState(callingPid); assertCanControlDeviceState(callingPid); final long callingIdentity = Binder.clearCallingIdentity(); final long callingIdentity = Binder.clearCallingIdentity(); try { try { Loading Loading
core/res/res/values/config.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -5778,4 +5778,7 @@ <string name="safety_protection_display_text"></string> <string name="safety_protection_display_text"></string> <!-- End safety protection resources to be overlaid --> <!-- End safety protection resources to be overlaid --> <!-- List of the labels of requestable device state config values --> <string-array name="config_deviceStatesAvailableForAppRequests"/> </resources> </resources>
core/res/res/values/symbols.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -4774,6 +4774,7 @@ <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" /> <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="drawable" name="ic_swap_horiz" /> <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> <java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" /> <!-- For app language picker --> <!-- For app language picker --> <java-symbol type="string" name="system_locale_title" /> <java-symbol type="string" name="system_locale_title" /> Loading
services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +74 −8 Original line number Original line Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.os.ShellCallback; import android.util.Slog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils; Loading @@ -59,7 +60,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Optional; import java.util.Set; import java.util.WeakHashMap; import java.util.WeakHashMap; /** /** Loading Loading @@ -139,6 +142,8 @@ public final class DeviceStateManagerService extends SystemService { @GuardedBy("mLock") @GuardedBy("mLock") private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); private Set<Integer> mDeviceStatesAvailableForAppRequests; public DeviceStateManagerService(@NonNull Context context) { public DeviceStateManagerService(@NonNull Context context) { this(context, DeviceStatePolicy.Provider this(context, DeviceStatePolicy.Provider .fromResources(context.getResources()) .fromResources(context.getResources()) Loading @@ -164,6 +169,10 @@ public final class DeviceStateManagerService extends SystemService { public void onStart() { public void onStart() { publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService); publishLocalService(DeviceStateManagerInternal.class, new LocalService()); publishLocalService(DeviceStateManagerInternal.class, new LocalService()); synchronized (mLock) { readStatesAvailableForRequestFromApps(); } } } @VisibleForTesting @VisibleForTesting Loading Loading @@ -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 * 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. * 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) { private void assertCanControlDeviceState(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 final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp(); if (topApp == null || topApp.getPid() != callingPid) { if (topApp == null || topApp.getPid() != callingPid) { getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, "Permission required to request 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 { private final class DeviceStateProviderListener implements DeviceStateProvider.Listener { @Override @Override public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) { public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) { Loading Loading @@ -777,7 +843,7 @@ public final class DeviceStateManagerService extends SystemService { // Allow top processes to request a device state change // 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 // If the calling process ID is not the top app, then we check if this process // holds a permission to CONTROL_DEVICE_STATE // holds a permission to CONTROL_DEVICE_STATE checkCanControlDeviceState(callingPid); assertCanRequestDeviceState(callingPid, state); if (token == null) { if (token == null) { throw new IllegalArgumentException("Request token must not be null."); throw new IllegalArgumentException("Request token must not be null."); Loading @@ -797,7 +863,7 @@ public final class DeviceStateManagerService extends SystemService { // Allow top processes to cancel a device state change // 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 // If the calling process ID is not the top app, then we check if this process // holds a permission to CONTROL_DEVICE_STATE // holds a permission to CONTROL_DEVICE_STATE checkCanControlDeviceState(callingPid); assertCanControlDeviceState(callingPid); final long callingIdentity = Binder.clearCallingIdentity(); final long callingIdentity = Binder.clearCallingIdentity(); try { try { Loading