diff options
24 files changed, 223 insertions, 87 deletions
diff --git a/api/current.txt b/api/current.txt index f01e9317d59c..12a3f07febf7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44559,6 +44559,7 @@ package android.view { field public static final int STATE_DOZE_SUSPEND = 4; // 0x4 field public static final int STATE_OFF = 1; // 0x1 field public static final int STATE_ON = 2; // 0x2 + field public static final int STATE_ON_SUSPEND = 6; // 0x6 field public static final int STATE_UNKNOWN = 0; // 0x0 field public static final int STATE_VR = 5; // 0x5 } diff --git a/api/system-current.txt b/api/system-current.txt index e7af921382d1..0b34782dbbba 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -48294,6 +48294,7 @@ package android.view { field public static final int STATE_DOZE_SUSPEND = 4; // 0x4 field public static final int STATE_OFF = 1; // 0x1 field public static final int STATE_ON = 2; // 0x2 + field public static final int STATE_ON_SUSPEND = 6; // 0x6 field public static final int STATE_UNKNOWN = 0; // 0x0 field public static final int STATE_VR = 5; // 0x5 } diff --git a/api/test-current.txt b/api/test-current.txt index 9f02bc5b3668..4eba70cce70f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -45171,6 +45171,7 @@ package android.view { field public static final int STATE_DOZE_SUSPEND = 4; // 0x4 field public static final int STATE_OFF = 1; // 0x1 field public static final int STATE_ON = 2; // 0x2 + field public static final int STATE_ON_SUSPEND = 6; // 0x6 field public static final int STATE_UNKNOWN = 0; // 0x0 field public static final int STATE_VR = 5; // 0x5 } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 6a15adeda9ab..2a245d046486 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -680,8 +680,8 @@ public class DreamService extends Service implements Window.Callback { * * @return The screen state to use while dozing, such as {@link Display#STATE_ON}, * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, - * or {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} for the default - * behavior. + * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} + * for the default behavior. * * @see #setDozeScreenState * @hide For use by system UI components only. @@ -700,12 +700,18 @@ public class DreamService extends Service implements Window.Callback { * perform transitions between states while dozing to conserve power and * achieve various effects. * </p><p> - * It is recommended that the state be set to {@link Display#STATE_DOZE_SUSPEND} - * once the dream has completely finished drawing and before it releases its wakelock - * to allow the display hardware to be fully suspended. While suspended, the - * display will preserve its on-screen contents or hand off control to dedicated - * doze hardware if the devices supports it. If the doze suspend state is - * used, the dream must make sure to set the mode back + * Some devices will have dedicated hardware ("Sidekick") to animate + * the display content while the CPU sleeps. If the dream and the hardware support + * this, {@link Display#STATE_ON_SUSPEND} or {@link Display#STATE_DOZE_SUSPEND} + * will switch control to the Sidekick. + * </p><p> + * If not using Sidekick, it is recommended that the state be set to + * {@link Display#STATE_DOZE_SUSPEND} once the dream has completely + * finished drawing and before it releases its wakelock + * to allow the display hardware to be fully suspended. While suspended, + * the display will preserve its on-screen contents. + * </p><p> + * If the doze suspend state is used, the dream must make sure to set the mode back * to {@link Display#STATE_DOZE} or {@link Display#STATE_ON} before drawing again * since the display updates may be ignored and not seen by the user otherwise. * </p><p> @@ -716,8 +722,8 @@ public class DreamService extends Service implements Window.Callback { * * @param state The screen state to use while dozing, such as {@link Display#STATE_ON}, * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, - * or {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} for the default - * behavior. + * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} + * for the default behavior. * * @hide For use by system UI components only. */ diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index e7c3f92da830..6a44cdb9ee57 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -294,11 +294,10 @@ public final class Display { /** * Display state: The display is dozing in a suspended low power state; it is still - * on but is optimized for showing static system-provided content while the device - * is non-interactive. This mode may be used to conserve even more power by allowing - * the hardware to stop applying frame buffer updates from the graphics subsystem or - * to take over the display and manage it autonomously to implement low power always-on - * display functionality. + * on but the CPU is not updating it. This may be used in one of two ways: to show + * static system-provided content while the device is non-interactive, or to allow + * a "Sidekick" compute resource to update the display. For this reason, the + * CPU must not control the display in this mode. * * @see #getState * @see android.os.PowerManager#isInteractive @@ -313,6 +312,18 @@ public final class Display { */ public static final int STATE_VR = 5; + /** + * Display state: The display is in a suspended full power state; it is still + * on but the CPU is not updating it. This may be used in one of two ways: to show + * static system-provided content while the device is non-interactive, or to allow + * a "Sidekick" compute resource to update the display. For this reason, the + * CPU must not control the display in this mode. + * + * @see #getState + * @see android.os.PowerManager#isInteractive + */ + public static final int STATE_ON_SUSPEND = 6; + /* The color mode constants defined below must be kept in sync with the ones in * system/core/include/system/graphics-base.h */ @@ -994,7 +1005,7 @@ public final class Display { * Gets the state of the display, such as whether it is on or off. * * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON}, - * {@link #STATE_DOZE}, {@link #STATE_DOZE_SUSPEND}, or + * {@link #STATE_DOZE}, {@link #STATE_DOZE_SUSPEND}, {@link #STATE_ON_SUSPEND}, or * {@link #STATE_UNKNOWN}. */ public int getState() { @@ -1113,6 +1124,8 @@ public final class Display { return "DOZE_SUSPEND"; case STATE_VR: return "VR"; + case STATE_ON_SUSPEND: + return "ON_SUSPEND"; default: return Integer.toString(state); } @@ -1120,11 +1133,11 @@ public final class Display { /** * Returns true if display updates may be suspended while in the specified - * display power state. + * display power state. In SUSPEND states, updates are absolutely forbidden. * @hide */ public static boolean isSuspendedState(int state) { - return state == STATE_OFF || state == STATE_DOZE_SUSPEND; + return state == STATE_OFF || state == STATE_DOZE_SUSPEND || state == STATE_ON_SUSPEND; } /** diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index cd8414791acb..5641009c1320 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -295,6 +295,12 @@ public class SurfaceControl { public static final int POWER_MODE_DOZE_SUSPEND = 3; /** + * Display power mode on: used while putting the screen into a suspended + * full power mode. Use only with {@link SurfaceControl#setDisplayPowerMode}. + */ + public static final int POWER_MODE_ON_SUSPEND = 4; + + /** * A value for windowType used to indicate that the window should be omitted from screenshots * and display mirroring. A temporary workaround until we express such things with * the hierarchy. diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 0535ebeb7615..2ec64a5ecdc4 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -9626,7 +9626,8 @@ public class BatteryStatsImpl extends BatteryStats { } public boolean isScreenOn(int state) { - return state == Display.STATE_ON || state == Display.STATE_VR; + return state == Display.STATE_ON || state == Display.STATE_VR + || state == Display.STATE_ON_SUSPEND; } public boolean isScreenOff(int state) { diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml index 548ee0561698..3c9f6eecc7d6 100644 --- a/core/res/res/layout/notification_material_action.xml +++ b/core/res/res/layout/notification_material_action.xml @@ -25,6 +25,7 @@ android:layout_marginStart="4dp" android:textColor="@color/notification_default_color" android:singleLine="true" + android:textAlignment="viewStart" android:ellipsize="end" android:background="@drawable/notification_material_action_background" /> diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml index 1f59ea02025b..817f298f0c6f 100644 --- a/core/res/res/layout/notification_material_action_tombstone.xml +++ b/core/res/res/layout/notification_material_action_tombstone.xml @@ -26,6 +26,7 @@ android:textColor="#555555" android:singleLine="true" android:ellipsize="end" + android:textAlignment="viewStart" android:alpha="0.5" android:enabled="false" android:background="@drawable/notification_material_action_background" diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java index 1cb255bf8973..a8262c8cc4c8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java +++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.XmlResourceParser; import android.icu.text.TimeZoneFormat; import android.icu.text.TimeZoneNames; +import android.support.annotation.VisibleForTesting; import android.support.v4.text.BidiFormatter; import android.support.v4.text.TextDirectionHeuristicsCompat; import android.text.SpannableString; @@ -32,6 +33,8 @@ import android.view.View; import com.android.settingslib.R; +import libcore.util.TimeZoneFinder; + import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayList; @@ -43,7 +46,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; -import libcore.util.TimeZoneFinder; /** * ZoneGetter is the utility class to get time zone and zone list, and both of them have display @@ -350,7 +352,8 @@ public class ZoneGetter { return gmtText; } - private static final class ZoneGetterData { + @VisibleForTesting + public static final class ZoneGetterData { public final String[] olsonIdsToDisplay; public final CharSequence[] gmtOffsetTexts; public final TimeZone[] timeZones; @@ -377,9 +380,13 @@ public class ZoneGetter { } // Create a lookup of local zone IDs. - List<String> zoneIds = - TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(locale.getCountry()); + final List<String> zoneIds = lookupTimeZoneIdsByCountry(locale.getCountry()); localZoneIds = new HashSet<>(zoneIds); } + + @VisibleForTesting + public List<String> lookupTimeZoneIdsByCountry(String country) { + return TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(country); + } } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml index 39cba742d9b5..3018a022e763 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml @@ -33,8 +33,10 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/Keyguard.TextView" + android:singleLine="true" android:ellipsize="marquee" android:visibility="gone" + android:gravity="center" androidprv:allCaps="@bool/kg_use_all_caps" /> <com.android.keyguard.EmergencyButton diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index 97c896591eb2..947f27deb84e 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -32,6 +32,7 @@ android:id="@+id/keyguard_sim" android:layout_width="match_parent" android:layout_height="wrap_content" + android:tint="@color/background_protected" android:src="@drawable/ic_lockscreen_sim"/> <include layout="@layout/keyguard_message_area" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index d4c5d7474aed..6f270b405c11 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -33,6 +33,7 @@ android:id="@+id/keyguard_sim" android:layout_width="match_parent" android:layout_height="wrap_content" + android:tint="@color/background_protected" android:src="@drawable/ic_lockscreen_sim"/> <include layout="@layout/keyguard_message_area" diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java index 5d0a9d70e99d..03b0082b96e7 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java @@ -33,8 +33,10 @@ public class DozeScreenStatePreventingAdapter extends DozeMachine.Service.Delega @Override public void setDozeScreenState(int state) { - if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) { + if (state == Display.STATE_DOZE) { state = Display.STATE_ON; + } else if (state == Display.STATE_DOZE_SUSPEND) { + state = Display.STATE_ON_SUSPEND; } super.setDozeScreenState(state); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java index 720e9f0a62d5..a17a95f6543d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java @@ -70,7 +70,7 @@ public class DozeScreenStatePreventingAdapterTest extends SysuiTestCase { @Test public void forwards_setDozeScreenState_doze_suspend() throws Exception { mWrapper.setDozeScreenState(Display.STATE_DOZE_SUSPEND); - verify(mInner).setDozeScreenState(Display.STATE_ON); + verify(mInner).setDozeScreenState(Display.STATE_ON_SUSPEND); } @Test @@ -95,4 +95,4 @@ public class DozeScreenStatePreventingAdapterTest extends SysuiTestCase { assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params)); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java index d78e739fce00..ed935619b2f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java @@ -74,6 +74,12 @@ public class DozeSuspendScreenStatePreventingAdapterTest extends SysuiTestCase { } @Test + public void forwards_setDozeScreenState_on_suspend() throws Exception { + mWrapper.setDozeScreenState(Display.STATE_ON_SUSPEND); + verify(mInner).setDozeScreenState(Display.STATE_ON_SUSPEND); + } + + @Test public void forwards_requestWakeUp() throws Exception { mWrapper.requestWakeUp(); verify(mInner).requestWakeUp(); @@ -95,4 +101,4 @@ public class DozeSuspendScreenStatePreventingAdapterTest extends SysuiTestCase { assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params)); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 1b61866f81b6..7a60a5d1e11d 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -49,6 +49,7 @@ import android.os.BatteryProperty; import android.os.Binder; import android.os.FileUtils; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBatteryPropertiesListener; import android.os.IBatteryPropertiesRegistrar; import android.os.IBinder; @@ -75,6 +76,7 @@ import java.io.PrintWriter; import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; /** @@ -176,6 +178,7 @@ public final class BatteryService extends SystemService { private HealthServiceWrapper mHealthServiceWrapper; private HealthHalCallback mHealthHalCallback; private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar; + private HandlerThread mHandlerThread; public BatteryService(Context context) { super(context); @@ -1195,14 +1198,13 @@ public final class BatteryService extends SystemService { Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD); private final IServiceNotification mNotification = new Notification(); + private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceRefresh"); // These variables are fixed after init. private Callback mCallback; private IHealthSupplier mHealthSupplier; private String mInstanceName; - private final Object mLastServiceSetLock = new Object(); // Last IHealth service received. - // set must be also be guarded with mLastServiceSetLock to ensure ordering. private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); /** @@ -1220,6 +1222,10 @@ public final class BatteryService extends SystemService { * Start monitoring registration of new IHealth services. Only instances that are in * {@code sAllInstances} and in device / framework manifest are used. This function should * only be called once. + * + * mCallback.onRegistration() is called synchronously (aka in init thread) before + * this method returns. + * * @throws RemoteException transaction error when talking to IServiceManager * @throws NoSuchElementException if one of the following cases: * - No service manager; @@ -1240,40 +1246,48 @@ public final class BatteryService extends SystemService { mCallback = callback; mHealthSupplier = healthSupplier; - traceBegin("HealthInitGetManager"); - try { - manager = managerSupplier.get(); - } finally { - traceEnd(); - } + // Initialize mLastService and call callback for the first time (in init thread) + IHealth newService = null; for (String name : sAllInstances) { - traceBegin("HealthInitGetTransport_" + name); + traceBegin("HealthInitGetService_" + name); try { - if (manager.getTransport(IHealth.kInterfaceName, name) != - IServiceManager.Transport.EMPTY) { - mInstanceName = name; - break; - } + newService = healthSupplier.get(name); + } catch (NoSuchElementException ex) { + /* ignored, handled below */ } finally { traceEnd(); } + if (newService != null) { + mInstanceName = name; + mLastService.set(newService); + break; + } } - if (mInstanceName == null) { + if (mInstanceName == null || newService == null) { throw new NoSuchElementException(String.format( "No IHealth service instance among %s is available. Perhaps no permission?", sAllInstances.toString())); } + mCallback.onRegistration(null, newService, mInstanceName); + // Register for future service registrations traceBegin("HealthInitRegisterNotification"); + mHandlerThread.start(); try { - manager.registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification); + managerSupplier.get().registerForNotifications( + IHealth.kInterfaceName, mInstanceName, mNotification); } finally { traceEnd(); } Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName); } + @VisibleForTesting + HandlerThread getHandlerThread() { + return mHandlerThread; + } + interface Callback { /** * This function is invoked asynchronously when a new and related IServiceNotification @@ -1302,7 +1316,7 @@ public final class BatteryService extends SystemService { */ interface IHealthSupplier { default IHealth get(String name) throws NoSuchElementException, RemoteException { - return IHealth.getService(name); + return IHealth.getService(name, true /* retry */); } } @@ -1312,18 +1326,27 @@ public final class BatteryService extends SystemService { boolean preexisting) { if (!IHealth.kInterfaceName.equals(interfaceName)) return; if (!mInstanceName.equals(instanceName)) return; - try { - // ensures the order of multiple onRegistration on different threads. - synchronized (mLastServiceSetLock) { - IHealth newService = mHealthSupplier.get(instanceName); - IHealth oldService = mLastService.getAndSet(newService); - Slog.i(TAG, "health: new instance registered " + instanceName); - mCallback.onRegistration(oldService, newService, instanceName); + + // This runnable only runs on mHandlerThread and ordering is ensured, hence + // no locking is needed inside the runnable. + mHandlerThread.getThreadHandler().post(new Runnable() { + @Override + public void run() { + try { + IHealth newService = mHealthSupplier.get(mInstanceName); + IHealth oldService = mLastService.getAndSet(newService); + + // preexisting may be inaccurate (race). Check for equality here. + if (Objects.equals(newService, oldService)) return; + + Slog.i(TAG, "health: new instance registered " + mInstanceName); + mCallback.onRegistration(oldService, newService, mInstanceName); + } catch (NoSuchElementException | RemoteException ex) { + Slog.e(TAG, "health: Cannot get instance '" + mInstanceName + + "': " + ex.getMessage() + ". Perhaps no permission?"); + } } - } catch (NoSuchElementException | RemoteException ex) { - Slog.e(TAG, "health: Cannot get instance '" + instanceName + "': " + - ex.getMessage() + ". Perhaps no permission?"); - } + }); } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index f930b523e626..600bc42e970c 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1120,6 +1120,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Dismiss the black surface without fanfare. mPowerState.setColorFadeLevel(1.0f); mPowerState.dismissColorFade(); + } else if (target == Display.STATE_ON_SUSPEND) { + // Want screen full-power and suspended. + // Wait for brightness animation to complete beforehand unless already + // suspended because we may not be able to change it after suspension. + if (mScreenBrightnessRampAnimator.isAnimating() + && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) { + return; + } + + // If not already suspending, temporarily set the state to on until the + // screen on is unblocked, then suspend. + if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) { + if (!setScreenState(Display.STATE_ON)) { + return; + } + setScreenState(Display.STATE_ON_SUSPEND); + } + + // Dismiss the black surface without fanfare. + mPowerState.setColorFadeLevel(1.0f); + mPowerState.dismissColorFade(); } else { // Want screen off. mPendingScreenOff = true; diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 9fdc35f5c6b6..eb9ff589d5f6 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -148,6 +148,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { return SurfaceControl.POWER_MODE_DOZE; case Display.STATE_DOZE_SUSPEND: return SurfaceControl.POWER_MODE_DOZE_SUSPEND; + case Display.STATE_ON_SUSPEND: + return SurfaceControl.POWER_MODE_ON_SUSPEND; default: return SurfaceControl.POWER_MODE_NORMAL; } @@ -470,6 +472,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { || oldState == Display.STATE_DOZE_SUSPEND) { setDisplayState(Display.STATE_DOZE); currentState = Display.STATE_DOZE; + } else if (state == Display.STATE_ON_SUSPEND + || oldState == Display.STATE_ON_SUSPEND) { + setDisplayState(Display.STATE_ON); + currentState = Display.STATE_ON; } else { return; // old state and new state is off } diff --git a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java index 92729dc08b63..6886985eec50 100644 --- a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java +++ b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java @@ -253,7 +253,8 @@ public class BurnInProtectionHelper implements DisplayManager.DisplayListener, public void onDisplayChanged(int displayId) { if (displayId == mDisplay.getDisplayId()) { if (mDisplay.getState() == Display.STATE_DOZE - || mDisplay.getState() == Display.STATE_DOZE_SUSPEND) { + || mDisplay.getState() == Display.STATE_DOZE_SUSPEND + || mDisplay.getState() == Display.STATE_ON_SUSPEND) { startBurnInProtection(); } else { cancelBurnInProtection(); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 8f23cf829366..416a60665a7f 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -2364,9 +2364,13 @@ public final class PowerManagerService extends SystemService if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; - if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND - && (mWakeLockSummary & WAKE_LOCK_DRAW) != 0) { - mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE; + if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) { + if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { + mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE; + } + if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { + mDisplayPowerRequest.dozeScreenState = Display.STATE_ON; + } } mDisplayPowerRequest.dozeScreenBrightness = mDozeScreenBrightnessOverrideFromDreamManager; @@ -4676,6 +4680,7 @@ public final class PowerManagerService extends SystemService case Display.STATE_OFF: case Display.STATE_DOZE: case Display.STATE_DOZE_SUSPEND: + case Display.STATE_ON_SUSPEND: case Display.STATE_ON: case Display.STATE_VR: break; diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java index daaad7a8c0b6..106f9e8c2705 100644 --- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java @@ -36,12 +36,14 @@ import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.invocation.InvocationOnMock; public class BatteryServiceTest extends AndroidTestCase { @Mock IServiceManager mMockedManager; @Mock IHealth mMockedHal; + @Mock IHealth mMockedHal2; @Mock BatteryService.HealthServiceWrapper.Callback mCallback; @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier; @@ -56,6 +58,12 @@ public class BatteryServiceTest extends AndroidTestCase { MockitoAnnotations.initMocks(this); } + @Override + public void tearDown() { + if (mWrapper != null) + mWrapper.getHandlerThread().quitSafely(); + } + public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) { return new ArgumentMatcher<T>() { @Override public boolean matches(T e) { @@ -70,42 +78,64 @@ public class BatteryServiceTest extends AndroidTestCase { private void initForInstances(String... instanceNamesArr) throws Exception { final Collection<String> instanceNames = Arrays.asList(instanceNamesArr); doAnswer((invocation) -> { - Slog.e("BatteryServiceTest", "health: onRegistration " + invocation.getArguments()[2]); - ((IServiceNotification)invocation.getArguments()[2]).onRegistration( - IHealth.kInterfaceName, - (String)invocation.getArguments()[1], - true /* preexisting */); + // technically, preexisting is ignored by + // BatteryService.HealthServiceWrapper.Notification, but still call it correctly. + sendNotification(invocation, true); + sendNotification(invocation, true); + sendNotification(invocation, false); return null; }).when(mMockedManager).registerForNotifications( eq(IHealth.kInterfaceName), argThat(isOneOf(instanceNames)), any(IServiceNotification.class)); - doReturn(mMockedHal).when(mMockedManager) - .get(eq(IHealth.kInterfaceName), argThat(isOneOf(instanceNames))); - - doReturn(IServiceManager.Transport.HWBINDER).when(mMockedManager) - .getTransport(eq(IHealth.kInterfaceName), argThat(isOneOf(instanceNames))); - doReturn(mMockedManager).when(mManagerSupplier).get(); - doReturn(mMockedHal).when(mHealthServiceSupplier) - .get(argThat(isOneOf(instanceNames))); + doReturn(mMockedHal) // init calls this + .doReturn(mMockedHal) // notification 1 + .doReturn(mMockedHal) // notification 2 + .doReturn(mMockedHal2) // notification 3 + .doThrow(new RuntimeException("Should not call getService for more than 4 times")) + .when(mHealthServiceSupplier).get(argThat(isOneOf(instanceNames))); mWrapper = new BatteryService.HealthServiceWrapper(); } + private void waitHandlerThreadFinish() throws Exception { + for (int i = 0; i < 5; i++) { + if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) { + return; + } + Thread.sleep(300); + } + assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()); + } + + private static void sendNotification(InvocationOnMock invocation, boolean preexisting) + throws Exception { + ((IServiceNotification)invocation.getArguments()[2]).onRegistration( + IHealth.kInterfaceName, + (String)invocation.getArguments()[1], + preexisting); + } + @SmallTest public void testWrapPreferVendor() throws Exception { initForInstances(VENDOR, HEALTHD); mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); - verify(mCallback).onRegistration(same(null), same(mMockedHal), eq(VENDOR)); + waitHandlerThreadFinish(); + verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR)); + verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString()); + verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR)); } @SmallTest public void testUseHealthd() throws Exception { initForInstances(HEALTHD); mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); - verify(mCallback).onRegistration(same(null), same(mMockedHal), eq(HEALTHD)); + waitHandlerThreadFinish(); + verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(HEALTHD)); + verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString()); + verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(HEALTHD)); } @SmallTest diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java index 038e295d8822..31d3db44d6fe 100644 --- a/telephony/java/android/telephony/ims/ImsServiceProxy.java +++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java @@ -305,19 +305,6 @@ public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeatur mStatusCallback = c; } - /** - * @return Returns true if the ImsService is ready to take commands, false otherwise. If this - * method returns false, it doesn't mean that the Binder connection is not available (use - * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands - * at this time. - * - * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take - * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}. - */ - public boolean isBinderReady() { - return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY; - } - @Override public boolean isBinderAlive() { return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java index bbd5f0279913..7ec92296448f 100644 --- a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java +++ b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java @@ -171,6 +171,19 @@ public class ImsServiceProxyCompat implements IMMTelFeature { return mBinder != null && mBinder.isBinderAlive(); } + /** + * @return Returns true if the ImsService is ready to take commands, false otherwise. If this + * method returns false, it doesn't mean that the Binder connection is not available (use + * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands + * at this time. + * + * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take + * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}. + */ + public boolean isBinderReady() { + return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY; + } + private IImsService getServiceInterface(IBinder b) { return IImsService.Stub.asInterface(b); } |