Loading core/tests/coretests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> <uses-permission android:name="android.permission.DEVICE_POWER"/> <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> Loading core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java 0 → 100644 +130 −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 com.android.internal.os; import static org.junit.Assert.assertTrue; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build; import android.os.ConditionVariable; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.SystemUtil; import com.google.android.collect.Sets; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; @RunWith(AndroidJUnit4.class) public class BatteryInputSuspendTest { public static final Set<String> SUPPORTED_DEVICES = Sets.newArraySet( "blueline", "crosshatch", "coral" ); private ChargerStateMonitor mChargerStateMonitor; @Before public void verifyCharging() { if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) { return; } mChargerStateMonitor = new ChargerStateMonitor(); assertTrue("Device must be connected to USB", mChargerStateMonitor.isCharging()); } @Test public void testSuspendInput() { if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) { return; } SystemUtil.runShellCommand("dumpsys battery suspend_input"); mChargerStateMonitor.waitForChargerState(/* isPluggedIn */false); } @After public void reenableCharging() { if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) { return; } mChargerStateMonitor.reset(); SystemUtil.runShellCommand("dumpsys battery reset"); mChargerStateMonitor.waitForChargerState(/* isPluggedIn */true); } private static class ChargerStateMonitor { private final Intent mBatteryMonitor; private final ConditionVariable mReady = new ConditionVariable(); private boolean mExpectedChargingState; ChargerStateMonitor() { Context context = InstrumentationRegistry.getInstrumentation().getContext(); mBatteryMonitor = context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (isCharging(intent) == mExpectedChargingState) { mReady.open(); } } }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } public boolean isCharging() { return isCharging(mBatteryMonitor); } private boolean isCharging(Intent batteryMonitor) { return batteryMonitor.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0; } void waitForChargerState(boolean isPluggedIn) { mExpectedChargingState = isPluggedIn; boolean charging = isCharging(); if (charging == mExpectedChargingState) { return; } boolean success = mReady.block(100000); assertTrue("Timed out waiting for charging state to change", success); } void reset() { mReady.close(); } } } services/core/java/com/android/server/BatteryService.java +23 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.os.BatteryManagerInternal; import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.DropBoxManager; import android.os.FileUtils; Loading @@ -59,6 +60,7 @@ import android.os.UEventObserver; import android.os.UserHandle; import android.provider.Settings; import android.service.battery.BatteryServiceDumpProto; import android.sysprop.PowerProperties; import android.util.EventLog; import android.util.MutableInt; import android.util.Slog; Loading Loading @@ -182,6 +184,7 @@ public final class BatteryService extends SystemService { private int mChargeStartLevel; private boolean mUpdatesStopped; private boolean mBatteryInputSuspended; private Led mLed; Loading Loading @@ -234,6 +237,8 @@ public final class BatteryService extends SystemService { invalidChargerObserver.startObserving( "DEVPATH=/devices/virtual/switch/invalid_charger"); } mBatteryInputSuspended = PowerProperties.battery_input_suspended().orElse(false); } @Override Loading Loading @@ -876,6 +881,10 @@ public final class BatteryService extends SystemService { pw.println(" reset [-f]"); pw.println(" Unfreeze battery state, returning to current hardware values."); pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); if (Build.IS_DEBUGGABLE) { pw.println(" disable_charge"); pw.println(" Suspend charging even if plugged in. "); } } static final int OPTION_FORCE_UPDATE = 1<<0; Loading Loading @@ -997,6 +1006,20 @@ public final class BatteryService extends SystemService { } finally { Binder.restoreCallingIdentity(ident); } if (mBatteryInputSuspended) { PowerProperties.battery_input_suspended(false); mBatteryInputSuspended = false; } } break; case "suspend_input": { if (!Build.IS_DEBUGGABLE) { throw new SecurityException( "battery suspend_input is only supported on debuggable builds"); } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); PowerProperties.battery_input_suspended(true); mBatteryInputSuspended = true; } break; default: return shell.handleDefaultCommands(cmd); Loading Loading
core/tests/coretests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> <uses-permission android:name="android.permission.DEVICE_POWER"/> <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> Loading
core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java 0 → 100644 +130 −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 com.android.internal.os; import static org.junit.Assert.assertTrue; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build; import android.os.ConditionVariable; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.SystemUtil; import com.google.android.collect.Sets; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; @RunWith(AndroidJUnit4.class) public class BatteryInputSuspendTest { public static final Set<String> SUPPORTED_DEVICES = Sets.newArraySet( "blueline", "crosshatch", "coral" ); private ChargerStateMonitor mChargerStateMonitor; @Before public void verifyCharging() { if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) { return; } mChargerStateMonitor = new ChargerStateMonitor(); assertTrue("Device must be connected to USB", mChargerStateMonitor.isCharging()); } @Test public void testSuspendInput() { if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) { return; } SystemUtil.runShellCommand("dumpsys battery suspend_input"); mChargerStateMonitor.waitForChargerState(/* isPluggedIn */false); } @After public void reenableCharging() { if (!SUPPORTED_DEVICES.contains(Build.DEVICE)) { return; } mChargerStateMonitor.reset(); SystemUtil.runShellCommand("dumpsys battery reset"); mChargerStateMonitor.waitForChargerState(/* isPluggedIn */true); } private static class ChargerStateMonitor { private final Intent mBatteryMonitor; private final ConditionVariable mReady = new ConditionVariable(); private boolean mExpectedChargingState; ChargerStateMonitor() { Context context = InstrumentationRegistry.getInstrumentation().getContext(); mBatteryMonitor = context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (isCharging(intent) == mExpectedChargingState) { mReady.open(); } } }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } public boolean isCharging() { return isCharging(mBatteryMonitor); } private boolean isCharging(Intent batteryMonitor) { return batteryMonitor.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0; } void waitForChargerState(boolean isPluggedIn) { mExpectedChargingState = isPluggedIn; boolean charging = isCharging(); if (charging == mExpectedChargingState) { return; } boolean success = mReady.block(100000); assertTrue("Timed out waiting for charging state to change", success); } void reset() { mReady.close(); } } }
services/core/java/com/android/server/BatteryService.java +23 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.os.BatteryManagerInternal; import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.DropBoxManager; import android.os.FileUtils; Loading @@ -59,6 +60,7 @@ import android.os.UEventObserver; import android.os.UserHandle; import android.provider.Settings; import android.service.battery.BatteryServiceDumpProto; import android.sysprop.PowerProperties; import android.util.EventLog; import android.util.MutableInt; import android.util.Slog; Loading Loading @@ -182,6 +184,7 @@ public final class BatteryService extends SystemService { private int mChargeStartLevel; private boolean mUpdatesStopped; private boolean mBatteryInputSuspended; private Led mLed; Loading Loading @@ -234,6 +237,8 @@ public final class BatteryService extends SystemService { invalidChargerObserver.startObserving( "DEVPATH=/devices/virtual/switch/invalid_charger"); } mBatteryInputSuspended = PowerProperties.battery_input_suspended().orElse(false); } @Override Loading Loading @@ -876,6 +881,10 @@ public final class BatteryService extends SystemService { pw.println(" reset [-f]"); pw.println(" Unfreeze battery state, returning to current hardware values."); pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); if (Build.IS_DEBUGGABLE) { pw.println(" disable_charge"); pw.println(" Suspend charging even if plugged in. "); } } static final int OPTION_FORCE_UPDATE = 1<<0; Loading Loading @@ -997,6 +1006,20 @@ public final class BatteryService extends SystemService { } finally { Binder.restoreCallingIdentity(ident); } if (mBatteryInputSuspended) { PowerProperties.battery_input_suspended(false); mBatteryInputSuspended = false; } } break; case "suspend_input": { if (!Build.IS_DEBUGGABLE) { throw new SecurityException( "battery suspend_input is only supported on debuggable builds"); } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); PowerProperties.battery_input_suspended(true); mBatteryInputSuspended = true; } break; default: return shell.handleDefaultCommands(cmd); Loading