diff options
34 files changed, 992 insertions, 229 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index ca5898670a40..9d116953992c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -120,6 +120,7 @@ package android { field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS"; field public static final java.lang.String HARDWARE_TEST = "android.permission.HARDWARE_TEST"; field public static final java.lang.String HDMI_CEC = "android.permission.HDMI_CEC"; + field public static final java.lang.String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"; field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS"; field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"; field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER"; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 3fd459958ed4..050aa4d66c14 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1403,15 +1403,14 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 0x00040000; /** - * Flag to indicate that this window is used as a task snapshot window. A task snapshot - * window is a starting window that gets shown with a screenshot from the previous state - * that is active until the app has drawn its first frame. - * - * <p>If this flag is set, SystemUI flags are ignored such that the real window behind can - * set the SystemUI flags. + * Flag to indicate that any window added by an application process that is of type + * {@link #TYPE_TOAST} or that requires + * {@link android.app.AppOpsManager#OP_SYSTEM_ALERT_WINDOW} permission should be hidden when + * this window is visible. * @hide */ - public static final int PRIVATE_FLAG_TASK_SNAPSHOT = 0x00080000; + @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) + public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000; /** * Flag to indicate that this window should be ignored when determining what parts of the diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 1f2e3d02144a..f21545fe8636 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3914,25 +3914,30 @@ public class Editor { menu.removeItem(TextView.ID_ASSIST); final TextClassification textClassification = getSelectionActionModeHelper().getTextClassification(); - if (textClassification != null) { - final Drawable icon = textClassification.getIcon(); - final CharSequence label = textClassification.getLabel(); - final OnClickListener onClickListener = - textClassification.getOnClickListener(); - final Intent intent = textClassification.getIntent(); - if ((icon != null || !TextUtils.isEmpty(label)) - && (onClickListener != null || intent != null)) { - menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, label) - .setIcon(icon) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - mMetricsLogger.write( - new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) - .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(textClassification.getLogType())); - } + if (canAssist()) { + menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, + textClassification.getLabel()) + .setIcon(textClassification.getIcon()) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + mMetricsLogger.write( + new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST) + .setType(MetricsEvent.TYPE_OPEN) + .setSubtype(textClassification.getLogType())); } } + private boolean canAssist() { + final TextClassification textClassification = + getSelectionActionModeHelper().getTextClassification(); + return mTextView.isDeviceProvisioned() + && textClassification != null + && (textClassification.getIcon() != null + || !TextUtils.isEmpty(textClassification.getLabel())) + && (textClassification.getOnClickListener() != null + || (textClassification.getIntent() != null + && mTextView.getContext().canStartActivityForResult())); + } + @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { getSelectionActionModeHelper().onSelectionAction(); diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9d9828ead705..89e137b7c399 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -792,19 +792,27 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) "--compiler-filter=", "-Ximage-compiler-option"); } - // Make sure there is a preloaded-classes file. - if (!hasFile("/system/etc/preloaded-classes")) { - ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n", - strerror(errno)); - return -1; - } - addOption("-Ximage-compiler-option"); - addOption("--image-classes=/system/etc/preloaded-classes"); - - // If there is a compiled-classes file, push it. - if (hasFile("/system/etc/compiled-classes")) { + // If there is a boot profile, it takes precedence over the image and preloaded classes. + if (hasFile("/system/etc/boot-image.prof")) { + addOption("-Ximage-compiler-option"); + addOption("--profile-file=/system/etc/boot-image.prof"); + addOption("-Ximage-compiler-option"); + addOption("--compiler-filter=speed-profile"); + } else { + // Make sure there is a preloaded-classes file. + if (!hasFile("/system/etc/preloaded-classes")) { + ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n", + strerror(errno)); + return -1; + } addOption("-Ximage-compiler-option"); - addOption("--compiled-classes=/system/etc/compiled-classes"); + addOption("--image-classes=/system/etc/preloaded-classes"); + + // If there is a compiled-classes file, push it. + if (hasFile("/system/etc/compiled-classes")) { + addOption("-Ximage-compiler-option"); + addOption("--compiled-classes=/system/etc/compiled-classes"); + } } property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, ""); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index be7934ba3fe5..6fa91abf71a5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2394,6 +2394,15 @@ <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to use + {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS} + to hide non-system-overlay windows. + <p>Not for use by third-party applications. + @hide + --> + <permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" + android:protectionLevel="signature|installer" /> + <!-- @SystemApi Allows an application to manage (create, destroy, Z-order) application tokens in the window manager. <p>Not for use by third-party applications. diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c0068d3b2957..02a2e8fc5313 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -247,6 +247,11 @@ --> <string name="doze_pickup_subtype_performs_proximity_check"></string> + <!-- Type of a sensor that provides a low-power estimate of the desired display + brightness, suitable to listen to while the device is asleep (e.g. during + always-on display) --> + <string name="doze_brightness_sensor_type" translatable="false"></string> + <!-- Doze: pulse parameter - how long does it take to fade in? --> <integer name="doze_pulse_duration_in">900</integer> diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index 8506734036dc..e92ed2f75d49 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -34,7 +34,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; import com.android.systemui.analytics.DataCollector; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.statusbar.StatusBarState; import java.io.PrintWriter; @@ -76,6 +75,7 @@ public class FalsingManager implements SensorEventListener { private boolean mSessionActive = false; private int mState = StatusBarState.SHADE; private boolean mScreenOn; + private boolean mShowingAod; private Runnable mPendingWtf; protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @@ -122,7 +122,7 @@ public class FalsingManager implements SensorEventListener { .append(" mState=").append(StatusBarState.toShortString(mState)) .toString() ); - return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD); + return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; } private boolean sessionEntrypoint() { @@ -144,6 +144,14 @@ public class FalsingManager implements SensorEventListener { } } + public void updateSessionActive() { + if (shouldSessionBeActive()) { + sessionEntrypoint(); + } else { + sessionExitpoint(false /* force */); + } + } + private void onSessionStart() { if (FalsingLog.ENABLED) { FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled()); @@ -249,6 +257,11 @@ public class FalsingManager implements SensorEventListener { return mEnforceBouncer; } + public void setShowingAod(boolean showingAod) { + mShowingAod = showingAod; + updateSessionActive(); + } + public void setStatusBarState(int state) { if (FalsingLog.ENABLED) { FalsingLog.i("setStatusBarState", new StringBuilder() @@ -257,11 +270,7 @@ public class FalsingManager implements SensorEventListener { .toString()); } mState = state; - if (shouldSessionBeActive()) { - sessionEntrypoint(); - } else { - sessionExitpoint(false /* force */); - } + updateSessionActive(); } public void onScreenTurningOn() { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 1cc10c22082a..4804ac23f3af 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -19,11 +19,14 @@ package com.android.systemui.doze; import android.app.AlarmManager; import android.app.Application; import android.content.Context; +import android.hardware.Sensor; import android.hardware.SensorManager; import android.os.Handler; import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.systemui.R; import com.android.systemui.SystemUIApplication; +import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -46,25 +49,38 @@ public class DozeFactory { WakeLock wakeLock = new DelayedWakeLock(handler, WakeLock.createPartial(context, "Doze")); - DozeMachine machine = new DozeMachine( - DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( - DozeScreenStatePreventingAdapter.wrapIfNeeded(dozeService, params), params), - config, - wakeLock); + DozeMachine.Service wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded( + DozeScreenStatePreventingAdapter.wrapIfNeeded(dozeService, params), params); + DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock); machine.setParts(new DozeMachine.Part[]{ - createDozeTriggers(context, sensorManager, host, config, params, handler, wakeLock, - machine), + new DozePauser(handler, machine, alarmManager), + new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)), + createDozeTriggers(context, sensorManager, host, alarmManager, config, params, + handler, wakeLock, machine), createDozeUi(context, host, wakeLock, machine, handler, alarmManager), + createDozeScreenState(wrappedService), + createDozeScreenBrightness(context, wrappedService, sensorManager, handler), }); return machine; } + private DozeMachine.Part createDozeScreenState(DozeMachine.Service service) { + return new DozeScreenState(service); + } + + private DozeMachine.Part createDozeScreenBrightness(Context context, + DozeMachine.Service service, SensorManager sensorManager, Handler handler) { + Sensor sensor = DozeSensors.findSensorWithType(sensorManager, + context.getString(R.string.doze_brightness_sensor_type)); + return new DozeScreenBrightness(context, service, sensorManager, sensor, handler); + } + private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager, - DozeHost host, AmbientDisplayConfiguration config, DozeParameters params, - Handler handler, WakeLock wakeLock, DozeMachine machine) { + DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config, + DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine) { boolean allowPulseTriggers = true; - return new DozeTriggers(context, machine, host, config, params, + return new DozeTriggers(context, machine, host, alarmManager, config, params, sensorManager, handler, wakeLock, allowPulseTriggers); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java new file mode 100644 index 000000000000..00ca9a48386e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 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.systemui.doze; + +import com.android.systemui.classifier.FalsingManager; + +/** + * Notifies FalsingManager of whether or not AOD is showing. + */ +public class DozeFalsingManagerAdapter implements DozeMachine.Part { + + private final FalsingManager mFalsingManager; + + public DozeFalsingManagerAdapter(FalsingManager falsingManager) { + mFalsingManager = falsingManager; + } + + @Override + public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + mFalsingManager.setShowingAod(isAodMode(newState)); + } + + private boolean isAodMode(DozeMachine.State state) { + switch (state) { + case DOZE_AOD: + case DOZE_AOD_PAUSING: + case DOZE_AOD_PAUSED: + return true; + default: + return false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 5526e6ba3f45..0be4eda8a069 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -60,13 +60,16 @@ public class DozeMachine { /** Doze is done. DozeService is finished. */ FINISH, /** AOD, but the display is temporarily off. */ - DOZE_AOD_PAUSED; + DOZE_AOD_PAUSED, + /** AOD, prox is near, transitions to DOZE_AOD_PAUSED after a timeout. */ + DOZE_AOD_PAUSING; boolean canPulse() { switch (this) { case DOZE: case DOZE_AOD: case DOZE_AOD_PAUSED: + case DOZE_AOD_PAUSING: return true; default: return false; @@ -93,6 +96,7 @@ public class DozeMachine { case DOZE_PULSING: return Display.STATE_ON; case DOZE_AOD: + case DOZE_AOD_PAUSING: return Display.STATE_DOZE_SUSPEND; default: return Display.STATE_UNKNOWN; @@ -222,7 +226,6 @@ public class DozeMachine { updatePulseReason(newState, oldState, pulseReason); performTransitionOnComponents(oldState, newState); - updateScreenState(newState); updateWakeLockState(newState); resolveIntermediateState(newState); @@ -284,7 +287,8 @@ public class DozeMachine { if (mState == State.FINISH) { return State.FINISH; } - if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD || mState == State.DOZE) + if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING + || mState == State.DOZE_AOD || mState == State.DOZE) && requestedState == State.DOZE_PULSE_DONE) { Log.i(TAG, "Dropping pulse done because current state is already done: " + mState); return mState; @@ -307,13 +311,6 @@ public class DozeMachine { } } - private void updateScreenState(State newState) { - int state = newState.screenState(); - if (state != Display.STATE_UNKNOWN) { - mDozeService.setDozeScreenState(state); - } - } - private void resolveIntermediateState(State state) { switch (state) { case INITIALIZED: @@ -360,5 +357,36 @@ public class DozeMachine { /** Request waking up. */ void requestWakeUp(); + + /** Set screen brightness */ + void setDozeScreenBrightness(int brightness); + + class Delegate implements Service { + private final Service mDelegate; + + public Delegate(Service delegate) { + mDelegate = delegate; + } + + @Override + public void finish() { + mDelegate.finish(); + } + + @Override + public void setDozeScreenState(int state) { + mDelegate.setDozeScreenState(state); + } + + @Override + public void requestWakeUp() { + mDelegate.requestWakeUp(); + } + + @Override + public void setDozeScreenBrightness(int brightness) { + mDelegate.setDozeScreenBrightness(brightness); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java new file mode 100644 index 000000000000..a33b454c6430 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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.systemui.doze; + +import android.app.AlarmManager; +import android.os.Handler; + +import com.android.systemui.util.AlarmTimeout; + +/** + * Moves the doze machine from the pausing to the paused state after a timeout. + */ +public class DozePauser implements DozeMachine.Part { + public static final String TAG = DozePauser.class.getSimpleName(); + private static final long TIMEOUT = 10 * 1000; + private final AlarmTimeout mPauseTimeout; + private final DozeMachine mMachine; + + public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager) { + mMachine = machine; + mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler); + } + + @Override + public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + switch (newState) { + case DOZE_AOD_PAUSING: + mPauseTimeout.schedule(TIMEOUT, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + break; + default: + mPauseTimeout.cancel(); + break; + } + } + + private void onTimeout() { + mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java new file mode 100644 index 000000000000..e461986da5e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 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.systemui.doze; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; + +/** + * Controls the screen brightness when dozing. + */ +public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListener { + private final Context mContext; + private final DozeMachine.Service mDozeService; + private final Handler mHandler; + private final SensorManager mSensorManager; + private final Sensor mLightSensor; + private boolean mRegistered; + + public DozeScreenBrightness(Context context, DozeMachine.Service service, + SensorManager sensorManager, Sensor lightSensor, Handler handler) { + mContext = context; + mDozeService = service; + mSensorManager = sensorManager; + mLightSensor = lightSensor; + mHandler = handler; + } + + @Override + public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + switch (newState) { + case INITIALIZED: + resetBrightnessToDefault(); + break; + case DOZE_AOD: + case DOZE_REQUEST_PULSE: + setLightSensorEnabled(true); + break; + case DOZE: + case DOZE_AOD_PAUSED: + setLightSensorEnabled(false); + resetBrightnessToDefault(); + break; + case FINISH: + setLightSensorEnabled(false); + break; + } + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (mRegistered) { + mDozeService.setDozeScreenBrightness(Math.max(1, (int) event.values[0])); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + private void resetBrightnessToDefault() { + mDozeService.setDozeScreenBrightness(mContext.getResources().getInteger( + com.android.internal.R.integer.config_screenBrightnessDoze)); + } + + private void setLightSensorEnabled(boolean enabled) { + if (enabled && !mRegistered && mLightSensor != null) { + mRegistered = mSensorManager.registerListener(this, mLightSensor, + SensorManager.SENSOR_DELAY_NORMAL, mHandler); + } else if (!enabled && mRegistered) { + mSensorManager.unregisterListener(this); + mRegistered = false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java new file mode 100644 index 000000000000..846ec27c451e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 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.systemui.doze; + +import android.content.Context; +import android.view.Display; + +/** + * Controls the screen when dozing. + */ +public class DozeScreenState implements DozeMachine.Part { + private final DozeMachine.Service mDozeService; + + public DozeScreenState(DozeMachine.Service service) { + mDozeService = service; + } + + @Override + public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + int screenState = newState.screenState(); + if (screenState != Display.STATE_UNKNOWN) { + mDozeService.setDozeScreenState(screenState); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java index ad5897aa71c2..5d0a9d70e99d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java @@ -24,18 +24,11 @@ import com.android.systemui.statusbar.phone.DozeParameters; /** * Prevents usage of doze screen states on devices that don't support them. */ -public class DozeScreenStatePreventingAdapter implements DozeMachine.Service { - - private final DozeMachine.Service mInner; +public class DozeScreenStatePreventingAdapter extends DozeMachine.Service.Delegate { @VisibleForTesting DozeScreenStatePreventingAdapter(DozeMachine.Service inner) { - mInner = inner; - } - - @Override - public void finish() { - mInner.finish(); + super(inner); } @Override @@ -43,12 +36,7 @@ public class DozeScreenStatePreventingAdapter implements DozeMachine.Service { if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) { state = Display.STATE_ON; } - mInner.setDozeScreenState(state); - } - - @Override - public void requestWakeUp() { - mInner.requestWakeUp(); + super.setDozeScreenState(state); } /** diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 23da716706d3..67de020cdfde 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -18,6 +18,7 @@ package com.android.systemui.doze; import android.annotation.AnyThread; import android.app.ActivityManager; +import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -29,6 +30,7 @@ import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; import android.net.Uri; import android.os.Handler; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -38,6 +40,7 @@ import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; import java.io.PrintWriter; @@ -51,6 +54,7 @@ public class DozeSensors { private static final String TAG = "DozeSensors"; private final Context mContext; + private final AlarmManager mAlarmManager; private final SensorManager mSensorManager; private final TriggerSensor[] mSensors; private final ContentResolver mResolver; @@ -65,10 +69,12 @@ public class DozeSensors { private final ProxSensor mProxSensor; - public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters, + public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager, + DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback, Consumer<Boolean> proxCallback) { mContext = context; + mAlarmManager = alarmManager; mSensorManager = sensorManager; mDozeParameters = dozeParameters; mConfig = config; @@ -100,10 +106,14 @@ public class DozeSensors { } private Sensor findSensorWithType(String type) { + return findSensorWithType(mSensorManager, type); + } + + static Sensor findSensorWithType(SensorManager sensorManager, String type) { if (TextUtils.isEmpty(type)) { return null; } - List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); + List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL); for (Sensor s : sensorList) { if (type.equals(s.getStringType())) { return s; @@ -140,7 +150,7 @@ public class DozeSensors { } public void setProxListening(boolean listen) { - mProxSensor.setRegistered(listen); + mProxSensor.setRequested(listen); } private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @@ -168,11 +178,23 @@ public class DozeSensors { private class ProxSensor implements SensorEventListener { + static final long COOLDOWN_TRIGGER = 2 * 1000; + static final long COOLDOWN_PERIOD = 5 * 1000; + + boolean mRequested; boolean mRegistered; Boolean mCurrentlyFar; + long mLastNear; + final AlarmTimeout mCooldownTimer; - void setRegistered(boolean register) { - if (mRegistered == register) { + + public ProxSensor() { + mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered, + "prox_cooldown", mHandler); + } + + void setRequested(boolean requested) { + if (mRequested == requested) { // Send an update even if we don't re-register. mHandler.post(() -> { if (mCurrentlyFar != null) { @@ -181,6 +203,18 @@ public class DozeSensors { }); return; } + mRequested = requested; + updateRegistered(); + } + + private void updateRegistered() { + setRegistered(mRequested && !mCooldownTimer.isScheduled()); + } + + private void setRegistered(boolean register) { + if (mRegistered == register) { + return; + } if (register) { mRegistered = mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY), @@ -196,6 +230,17 @@ public class DozeSensors { public void onSensorChanged(SensorEvent event) { mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange(); mProxCallback.accept(mCurrentlyFar); + + long now = SystemClock.elapsedRealtime(); + if (!mCurrentlyFar) { + mLastNear = now; + } else if (mCurrentlyFar && now - mLastNear < COOLDOWN_TRIGGER) { + // If the last near was very recent, we might be using more power for prox + // wakeups than we're saving from turning of the screen. Instead, turn it off + // for a while. + mCooldownTimer.schedule(COOLDOWN_PERIOD, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + updateRegistered(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index d9fb087a6041..98b110619a1c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -76,8 +76,6 @@ public class DozeService extends DreamService super.onDreamingStarted(); mDozeMachine.requestState(DozeMachine.State.INITIALIZED); startDozing(); - setDozeScreenBrightness(getResources().getInteger( - com.android.internal.R.integer.config_screenBrightnessDoze)); if (mDozePlugin != null) { mDozePlugin.onDreamingStarted(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java index 1e067974e3cc..1c6521f641bf 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java @@ -24,18 +24,11 @@ import com.android.systemui.statusbar.phone.DozeParameters; /** * Prevents usage of doze screen states on devices that don't support them. */ -public class DozeSuspendScreenStatePreventingAdapter implements DozeMachine.Service { - - private final DozeMachine.Service mInner; +public class DozeSuspendScreenStatePreventingAdapter extends DozeMachine.Service.Delegate { @VisibleForTesting DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner) { - mInner = inner; - } - - @Override - public void finish() { - mInner.finish(); + super(inner); } @Override @@ -43,12 +36,7 @@ public class DozeSuspendScreenStatePreventingAdapter implements DozeMachine.Serv if (state == Display.STATE_DOZE_SUSPEND) { state = Display.STATE_DOZE; } - mInner.setDozeScreenState(state); - } - - @Override - public void requestWakeUp() { - mInner.requestWakeUp(); + super.setDozeScreenState(state); } /** diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 8d1d6e0ce460..610eaffec34b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -16,6 +16,7 @@ package com.android.systemui.doze; +import android.app.AlarmManager; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -70,7 +71,7 @@ public class DozeTriggers implements DozeMachine.Part { public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost, - AmbientDisplayConfiguration config, + AlarmManager alarmManager, AmbientDisplayConfiguration config, DozeParameters dozeParameters, SensorManager sensorManager, Handler handler, WakeLock wakeLock, boolean allowPulseTriggers) { mContext = context; @@ -82,8 +83,8 @@ public class DozeTriggers implements DozeMachine.Part { mHandler = handler; mWakeLock = wakeLock; mAllowPulseTriggers = allowPulseTriggers; - mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config, - wakeLock, this::onSensor, this::onProximityFar); + mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, + config, wakeLock, this::onSensor, this::onProximityFar); mUiModeManager = mContext.getSystemService(UiModeManager.class); } @@ -152,18 +153,22 @@ public class DozeTriggers implements DozeMachine.Part { private void onProximityFar(boolean far) { final boolean near = !far; - DozeMachine.State state = mMachine.getState(); + final DozeMachine.State state = mMachine.getState(); + final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); + final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); + final boolean aod = (state == DozeMachine.State.DOZE_AOD); + if (near && state == DozeMachine.State.DOZE_PULSING) { if (DEBUG) Log.i(TAG, "Prox NEAR, ending pulse"); DozeLog.tracePulseCanceledByProx(mContext); mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE); } - if (far && state == DozeMachine.State.DOZE_AOD_PAUSED) { + if (far && (paused || pausing)) { if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD"); mMachine.requestState(DozeMachine.State.DOZE_AOD); - } else if (near && state == DozeMachine.State.DOZE_AOD) { + } else if (near && aod) { if (DEBUG) Log.i(TAG, "Prox NEAR, pausing AOD"); - mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED); + mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING); } } @@ -192,6 +197,7 @@ public class DozeTriggers implements DozeMachine.Part { } break; case DOZE_AOD_PAUSED: + case DOZE_AOD_PAUSING: mDozeSensors.setProxListening(true); mDozeSensors.setListening(false); break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index cf87fca56f39..1dc37cdbca7a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -23,6 +23,7 @@ import android.os.SystemClock; import android.text.format.Formatter; import android.util.Log; +import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; import java.util.Calendar; @@ -35,26 +36,23 @@ public class DozeUi implements DozeMachine.Part { private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min private final Context mContext; - private final AlarmManager mAlarmManager; private final DozeHost mHost; private final Handler mHandler; private final WakeLock mWakeLock; private final DozeMachine mMachine; - private final AlarmManager.OnAlarmListener mTimeTick; + private final AlarmTimeout mTimeTicker; - private boolean mTimeTickScheduled = false; private long mLastTimeTickElapsed = 0; public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, WakeLock wakeLock, DozeHost host, Handler handler) { mContext = context; - mAlarmManager = alarmManager; mMachine = machine; mWakeLock = wakeLock; mHost = host; mHandler = handler; - mTimeTick = this::onTimeTick; + mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); } private void pulseWhileDozing(int reason) { @@ -76,6 +74,7 @@ public class DozeUi implements DozeMachine.Part { public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD: + case DOZE_AOD_PAUSING: scheduleTimeTick(); break; case DOZE: @@ -112,25 +111,21 @@ public class DozeUi implements DozeMachine.Part { } private void scheduleTimeTick() { - if (mTimeTickScheduled) { + if (mTimeTicker.isScheduled()) { return; } long delta = roundToNextMinute(System.currentTimeMillis()) - System.currentTimeMillis(); - mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler); - - mTimeTickScheduled = true; + mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); mLastTimeTickElapsed = SystemClock.elapsedRealtime(); } private void unscheduleTimeTick() { - if (!mTimeTickScheduled) { + if (!mTimeTicker.isScheduled()) { return; } verifyLastTimeTick(); - mAlarmManager.cancel(mTimeTick); - mTimeTickScheduled = false; + mTimeTicker.cancel(); } private void verifyLastTimeTick() { @@ -153,10 +148,6 @@ public class DozeUi implements DozeMachine.Part { } private void onTimeTick() { - if (!mTimeTickScheduled) { - // Alarm was canceled, but we still got the callback. Ignore. - return; - } verifyLastTimeTick(); mHost.dozeTimeTick(); @@ -164,7 +155,6 @@ public class DozeUi implements DozeMachine.Part { // Keep wakelock until a frame has been pushed. mHandler.post(mWakeLock.wrap(() -> {})); - mTimeTickScheduled = false; scheduleTimeTick(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index 09ae521ceb2b..6f2c6e0bb60a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -131,9 +131,6 @@ public class NavigationBarInflaterView extends FrameLayout mRot90.setId(R.id.rot90); addView(mRot90); updateAlternativeOrder(); - if (getParent() instanceof NavigationBarView) { - ((NavigationBarView) getParent()).updateRotatedViews(); - } } protected String getDefaultLayout() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 426444158b11..03dd41ff1ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -558,7 +558,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void onFinishInflate() { mNavigationInflaterView = (NavigationBarInflaterView) findViewById( R.id.navigation_inflater); - updateRotatedViews(); mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); @@ -567,16 +566,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mDockedStackExists = exists; updateRecentsIcon(); })); + updateRotatedViews(); } - void updateRotatedViews() { + private void updateRotatedViews() { mRotatedViews[Surface.ROTATION_0] = mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90); - - mCurrentRotation = -1; - reorient(); } public boolean needsReorient(int rotation) { @@ -615,9 +612,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone); - if (getRootView() instanceof NavigationBarFrame) { - ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); - } + + ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone); + mDeadZone.setDisplayRotation(mCurrentRotation); // force the low profile & disabled states into compliance @@ -757,6 +754,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + reorient(); onPluginDisconnected(null); // Create default gesture helper Dependency.get(PluginManager.class).addPluginListener(this, NavGesture.class, false /* Only one */); diff --git a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java new file mode 100644 index 000000000000..f7f61aff9849 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 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.systemui.util; + +import android.app.AlarmManager; +import android.os.Handler; +import android.os.SystemClock; + +/** + * Schedules a timeout through AlarmManager. Ensures that the timeout is called even when + * the device is asleep. + */ +public class AlarmTimeout implements AlarmManager.OnAlarmListener { + + public static final int MODE_CRASH_IF_SCHEDULED = 0; + public static final int MODE_IGNORE_IF_SCHEDULED = 1; + public static final int MODE_RESCHEDULE_IF_SCHEDULED = 2; + + private final AlarmManager mAlarmManager; + private final AlarmManager.OnAlarmListener mListener; + private final String mTag; + private final Handler mHandler; + private boolean mScheduled; + + public AlarmTimeout(AlarmManager alarmManager, AlarmManager.OnAlarmListener listener, + String tag, Handler handler) { + mAlarmManager = alarmManager; + mListener = listener; + mTag = tag; + mHandler = handler; + } + + public void schedule(long timeout, int mode) { + switch (mode) { + case MODE_CRASH_IF_SCHEDULED: + if (mScheduled) { + throw new IllegalStateException(mTag + " timeout is already scheduled"); + } + break; + case MODE_IGNORE_IF_SCHEDULED: + if (mScheduled) { + return; + } + break; + case MODE_RESCHEDULE_IF_SCHEDULED: + if (mScheduled) { + cancel(); + } + break; + default: + throw new IllegalArgumentException("Illegal mode: " + mode); + } + + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + timeout, mTag, this, mHandler); + mScheduled = true; + } + + public boolean isScheduled() { + return mScheduled; + } + + public void cancel() { + if (mScheduled) { + mAlarmManager.cancel(this); + mScheduled = false; + } + } + + @Override + public void onAlarm() { + if (!mScheduled) { + // We canceled the alarm, but it still fired. Ignore. + return; + } + mScheduled = false; + mListener.onAlarm(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 3a09ce07288e..368c814f8e0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -215,54 +215,6 @@ public class DozeMachineTest extends SysuiTestCase { } @Test - public void testScreen_offInDoze() { - mMachine.requestState(INITIALIZED); - - mMachine.requestState(DOZE); - - assertEquals(Display.STATE_OFF, mServiceFake.screenState); - } - - @Test - public void testScreen_onInAod() { - mMachine.requestState(INITIALIZED); - - mMachine.requestState(DOZE_AOD); - - assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState); - } - - @Test - public void testScreen_onInPulse() { - mMachine.requestState(INITIALIZED); - - mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); - mMachine.requestState(DOZE_PULSING); - - assertEquals(Display.STATE_ON, mServiceFake.screenState); - } - - @Test - public void testScreen_offInRequestPulseWithoutAoD() { - mMachine.requestState(INITIALIZED); - - mMachine.requestState(DOZE); - mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); - - assertEquals(Display.STATE_OFF, mServiceFake.screenState); - } - - @Test - public void testScreen_onInRequestPulseWithoutAoD() { - mMachine.requestState(INITIALIZED); - - mMachine.requestState(DOZE_AOD); - mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION); - - assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState); - } - - @Test public void testTransitions_canRequestTransitions() { mMachine.requestState(INITIALIZED); mMachine.requestState(DOZE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java new file mode 100644 index 000000000000..514dc8b94de5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 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.systemui.doze; + +import static com.android.systemui.doze.DozeMachine.State.DOZE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; +import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING; +import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE; +import static com.android.systemui.doze.DozeMachine.State.FINISH; +import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; +import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import android.os.PowerManager; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.utils.hardware.FakeSensorManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DozeScreenBrightnessTest extends SysuiTestCase { + + DozeServiceFake mServiceFake; + DozeScreenBrightness mScreen; + FakeSensorManager.FakeGenericSensor mSensor; + FakeSensorManager mSensorManager; + + @Before + public void setUp() throws Exception { + mServiceFake = new DozeServiceFake(); + mSensorManager = new FakeSensorManager(mContext); + mSensor = mSensorManager.getFakeLightSensor(); + mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, + mSensor.getSensor(), null /* handler */); + } + + @Test + public void testInitialize_setsScreenBrightnessToValidValue() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + + assertNotEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightness); + assertTrue(mServiceFake.screenBrightness <= PowerManager.BRIGHTNESS_ON); + } + + @Test + public void testAod_usesLightSensor() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mSensor.sendSensorEvent(1000); + + assertEquals(1000, mServiceFake.screenBrightness); + } + + @Test + public void testPausingAod_pausesLightSensor() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mSensor.sendSensorEvent(1000); + + mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED); + + mSensor.sendSensorEvent(1001); + + assertNotEquals(1001, mServiceFake.screenBrightness); + } + + @Test + public void testPausingAod_resetsBrightness() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mSensor.sendSensorEvent(1000); + + mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED); + + assertNotEquals(1000, mServiceFake.screenBrightness); + } + + @Test + public void testPulsing_usesLightSensor() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); + + mSensor.sendSensorEvent(1000); + + assertEquals(1000, mServiceFake.screenBrightness); + } + + @Test + public void testDozingAfterPulsing_pausesLightSensor() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); + mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING); + mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE); + mScreen.transitionTo(DOZE_PULSE_DONE, DOZE); + + mSensor.sendSensorEvent(1000); + + assertNotEquals(1000, mServiceFake.screenBrightness); + } + + @Test + public void testNullSensor() throws Exception { + mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, + null /* sensor */, null /* handler */); + + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED); + } + + @Test + public void testNoBrightnessDeliveredAfterFinish() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.transitionTo(DOZE_AOD, FINISH); + + mSensor.sendSensorEvent(1000); + + assertNotEquals(1000, mServiceFake.screenBrightness); + } + + @Test + public void testBrightness_atLeastOne() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mSensor.sendSensorEvent(0); + + assertTrue("Brightness must be at least 1, but was " + mServiceFake.screenBrightness, + mServiceFake.screenBrightness >= 1); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java new file mode 100644 index 000000000000..203876b9a2f5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 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.systemui.doze; + +import static com.android.systemui.doze.DozeMachine.State.DOZE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING; +import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE; +import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; +import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; + +import static org.junit.Assert.assertEquals; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.Display; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DozeScreenStateTest extends SysuiTestCase { + + DozeServiceFake mServiceFake; + DozeScreenState mScreen; + + @Before + public void setUp() throws Exception { + mServiceFake = new DozeServiceFake(); + mScreen = new DozeScreenState(mServiceFake); + } + + @Test + public void testScreen_offInDoze() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + + assertEquals(Display.STATE_OFF, mServiceFake.screenState); + } + + @Test + public void testScreen_onInAod() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState); + } + + @Test + public void testScreen_onInPulse() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + + mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); + mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING); + + assertEquals(Display.STATE_ON, mServiceFake.screenState); + } + + @Test + public void testScreen_offInRequestPulseWithoutAoD() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE); + + mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); + + assertEquals(Display.STATE_OFF, mServiceFake.screenState); + } + + @Test + public void testScreen_onInRequestPulseWithAoD() { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE); + + assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState); + } + +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java index c1e7fe46de8e..34026b0b6145 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java @@ -16,6 +16,7 @@ package com.android.systemui.doze; +import android.os.PowerManager; import android.view.Display; public class DozeServiceFake implements DozeMachine.Service { @@ -23,6 +24,7 @@ public class DozeServiceFake implements DozeMachine.Service { public boolean finished; public int screenState; public boolean requestedWakeup; + public int screenBrightness; public DozeServiceFake() { reset(); @@ -38,13 +40,19 @@ public class DozeServiceFake implements DozeMachine.Service { screenState = state; } - public void reset() { - finished = false; - screenState = Display.STATE_UNKNOWN; - } - @Override public void requestWakeUp() { requestedWakeup = true; } + + @Override + public void setDozeScreenBrightness(int brightness) { + screenBrightness = brightness; + } + + public void reset() { + finished = false; + screenState = Display.STATE_UNKNOWN; + screenBrightness = PowerManager.BRIGHTNESS_DEFAULT; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 8641faca5d6e..a8ea1c0770da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AlarmManager; import android.app.Instrumentation; import android.os.Handler; import android.os.Looper; @@ -56,6 +57,7 @@ public class DozeTriggersTest extends SysuiTestCase { private Handler mHandler; private WakeLock mWakeLock; private Instrumentation mInstrumentation; + private AlarmManager mAlarmManager; @BeforeClass public static void setupSuite() { @@ -67,6 +69,7 @@ public class DozeTriggersTest extends SysuiTestCase { public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); mMachine = mock(DozeMachine.class); + mAlarmManager = mock(AlarmManager.class); mHost = new DozeHostFake(); mConfig = DozeConfigurationUtil.createMockConfig(); mParameters = DozeConfigurationUtil.createMockParameters(); @@ -75,7 +78,7 @@ public class DozeTriggersTest extends SysuiTestCase { mWakeLock = new WakeLockFake(); mInstrumentation.runOnMainSync(() -> { - mTriggers = new DozeTriggers(mContext, mMachine, mHost, + mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters, mSensors, mHandler, mWakeLock, true); }); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java index 30be6658d4b6..a4ae166d0675 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java @@ -30,13 +30,15 @@ import android.os.MemoryFile; import android.os.SystemClock; import android.util.ArraySet; -import com.google.android.collect.Lists; +import com.android.internal.util.Preconditions; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; /** * Rudimentary fake for SensorManager @@ -49,6 +51,8 @@ import java.util.List; public class FakeSensorManager extends SensorManager { private final MockProximitySensor mMockProximitySensor; + private final FakeGenericSensor mFakeLightSensor; + private final FakeGenericSensor[] mSensors; public FakeSensorManager(Context context) throws Exception { Sensor proxSensor = context.getSystemService(SensorManager.class) @@ -57,13 +61,21 @@ public class FakeSensorManager extends SensorManager { // No prox? Let's create a fake one! proxSensor = createSensor(Sensor.TYPE_PROXIMITY); } - mMockProximitySensor = new MockProximitySensor(proxSensor); + + mSensors = new FakeGenericSensor[]{ + mMockProximitySensor = new MockProximitySensor(proxSensor), + mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT)), + }; } public MockProximitySensor getMockProximitySensor() { return mMockProximitySensor; } + public FakeGenericSensor getFakeLightSensor() { + return mFakeLightSensor; + } + @Override public Sensor getDefaultSensor(int type) { Sensor s = super.getDefaultSensor(type); @@ -77,7 +89,10 @@ public class FakeSensorManager extends SensorManager { @Override protected List<Sensor> getFullSensorList() { - return Lists.newArrayList(mMockProximitySensor.sensor); + return Arrays + .stream(mSensors) + .map(i -> i.mSensor) + .collect(Collectors.toList()); } @Override @@ -87,8 +102,11 @@ public class FakeSensorManager extends SensorManager { @Override protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { - if (sensor == mMockProximitySensor.sensor || sensor == null) { - mMockProximitySensor.listeners.remove(listener); + Preconditions.checkNotNull(listener); + for (FakeGenericSensor s : mSensors) { + if (sensor == null || s.mSensor == sensor) { + s.mListeners.remove(listener); + } } } @@ -96,9 +114,13 @@ public class FakeSensorManager extends SensorManager { protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs, int reservedFlags) { - if (sensor == mMockProximitySensor.sensor) { - mMockProximitySensor.listeners.add(listener); - return true; + Preconditions.checkNotNull(sensor); + Preconditions.checkNotNull(listener); + for (FakeGenericSensor s : mSensors) { + if (s.mSensor == sensor) { + s.mListeners.add(listener); + return true; + } } return false; } @@ -196,18 +218,35 @@ public class FakeSensorManager extends SensorManager { setter.invoke(sensor, type); } - public class MockProximitySensor { - final Sensor sensor; - final ArraySet<SensorEventListener> listeners = new ArraySet<>(); + public class MockProximitySensor extends FakeGenericSensor { private MockProximitySensor(Sensor sensor) { - this.sensor = sensor; + super(sensor); } public void sendProximityResult(boolean far) { - SensorEvent event = createSensorEvent(1); - event.values[0] = far ? sensor.getMaximumRange() : 0; - for (SensorEventListener listener : listeners) { + sendSensorEvent(far ? getSensor().getMaximumRange() : 0); + } + } + + public class FakeGenericSensor { + + private final Sensor mSensor; + private final ArraySet<SensorEventListener> mListeners = new ArraySet<>(); + + public FakeGenericSensor( + Sensor sensor) { + this.mSensor = sensor; + } + + public Sensor getSensor() { + return mSensor; + } + + public void sendSensorEvent(float... values) { + SensorEvent event = createSensorEvent(values.length); + System.arraycopy(values, 0, event.values, 0, values.length); + for (SensorEventListener listener : mListeners) { listener.onSensorChanged(event); } } @@ -222,7 +261,7 @@ public class FakeSensorManager extends SensorManager { } catch (Exception e) { throw new RuntimeException(e); } - event.sensor = sensor; + event.sensor = mSensor; event.timestamp = SystemClock.elapsedRealtimeNanos(); return event; diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 61057dd25444..75206e48aa8b 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -86,6 +86,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ACTIVE_LOG_MAX_SIZE = 20; private static final int CRASH_LOG_MAX_SIZE = 100; private static final String REASON_AIRPLANE_MODE = "airplane mode"; + private static final String REASON_DISALLOWED = "disallowed by system"; + private static final String REASON_SHARING_DISALLOWED = "sharing disallowed by system"; private static final String REASON_RESTARTED = "automatic restart"; private static final String REASON_START_CRASH = "turn-on crash"; private static final String REASON_SYSTEM_BOOT = "system boot"; @@ -227,25 +229,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { - if (!UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, - UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_BLUETOOTH_SHARING)) { - return; // No relevant changes, nothing to do. - } - final boolean disallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, + UserManager.DISALLOW_BLUETOOTH_SHARING)) { + updateOppLauncherComponentState(userId, newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH_SHARING)); + } - // DISALLOW_BLUETOOTH is a global restriction that can only be set by DO or PO on the - // system user, so we only look at the system user. - if (userId == UserHandle.USER_SYSTEM && disallowed && (mEnable || mEnableExternal)) { - try { - disable(null /* packageName */, true /* persist */); - } catch (RemoteException e) { - Slog.w(TAG, "Exception when disabling Bluetooth", e); + // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user. + if (userId == UserHandle.USER_SYSTEM && + UserRestrictionsUtils.restrictionsChanged( + prevRestrictions, newRestrictions, UserManager.DISALLOW_BLUETOOTH)) { + if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH)) { + updateOppLauncherComponentState(userId, true); // Sharing disallowed + sendDisableMsg(REASON_DISALLOWED); + } else { + updateOppLauncherComponentState(userId, newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH_SHARING)); } } - final boolean sharingDisallowed = disallowed - || newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING); - updateOppLauncherComponentState(userId, sharingDisallowed); } }; @@ -2118,7 +2121,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; try { final IPackageManager imp = AppGlobals.getPackageManager(); - imp.setComponentEnabledSetting(oppLauncherComponent, newState, 0 /* flags */, userId); + imp.setComponentEnabledSetting(oppLauncherComponent, newState, + PackageManager.DONT_KILL_APP, userId); } catch (Exception e) { // The component was not found, do nothing. } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 22b0f5bcdf07..7a8c2f91b4e9 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -82,6 +83,7 @@ public class Session extends IWindowSession.Stub // Set of visible alert window surfaces connected to this session. private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>(); final boolean mCanAddInternalSystemWindow; + final boolean mCanHideNonSystemOverlayWindows; private AlertWindowNotification mAlertWindowNotification; private boolean mShowingAlertWindowNotificationAllowed; private boolean mClientDead = false; @@ -99,6 +101,8 @@ public class Session extends IWindowSession.Stub mLastReportedAnimatorScale = service.getCurrentAnimatorScale(); mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission( INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED; + mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission( + HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; StringBuilder sb = new StringBuilder(); sb.append("Session{"); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 469dab4e0a3e..802f9edc92dc 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -33,7 +33,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; @@ -166,8 +165,7 @@ class TaskSnapshotSurface implements StartingSurface { layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE; - layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT - | (windowPrivateFlags & PRIVATE_FLAG_INHERITS); + layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS; layoutParams.token = token.token; layoutParams.width = LayoutParams.MATCH_PARENT; layoutParams.height = LayoutParams.MATCH_PARENT; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 613c830f31f2..20d9286781bb 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -495,12 +495,15 @@ public class WindowManagerService extends IWindowManager.Stub */ Runnable mWaitingForDrawnCallback; + /** List of window currently causing non-system overlay windows to be hidden. */ + private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>(); + /** * Stores for each user whether screencapture is disabled * This array is essentially a cache for all userId for * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled} */ - SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<>(); + private SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<>(); IInputMethodManager mInputMethodManager; @@ -1715,6 +1718,7 @@ public class WindowManagerService extends IWindowManager.Stub } mPendingRemove.remove(win); mResizingWindows.remove(win); + updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */); mWindowsChanged = true; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Final remove of window: " + win); @@ -6484,6 +6488,21 @@ public class WindowManagerService extends IWindowManager.Stub ArrayList<WindowState> windows) { mRoot.dumpWindowsNoHeader(pw, dumpAll, windows); + if (!mHidingNonSystemOverlayWindows.isEmpty()) { + pw.println(); + pw.println(" Hiding System Alert Windows:"); + for (int i = mHidingNonSystemOverlayWindows.size() - 1; i >= 0; i--) { + final WindowState w = mHidingNonSystemOverlayWindows.get(i); + pw.print(" #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } if (mPendingRemove.size() > 0) { pw.println(); pw.println(" Remove pending for:"); @@ -7598,4 +7617,28 @@ public class WindowManagerService extends IWindowManager.Stub boolean hasWideColorGamutSupport() { return mHasWideColorGamutSupport; } + + void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) { + if (!win.hideNonSystemOverlayWindowsWhenVisible()) { + return; + } + final boolean systemAlertWindowsHidden = !mHidingNonSystemOverlayWindows.isEmpty(); + if (surfaceShown) { + if (!mHidingNonSystemOverlayWindows.contains(win)) { + mHidingNonSystemOverlayWindows.add(win); + } + } else { + mHidingNonSystemOverlayWindows.remove(win); + } + + final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); + + if (systemAlertWindowsHidden == hideSystemAlertWindows) { + return; + } + + mRoot.forAllWindows((w) -> { + w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); + }, false /* traverseTopToBottom */); + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d1fbf900ef70..001019b43f9c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -46,6 +46,7 @@ import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; @@ -59,7 +60,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED; import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM; import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; @@ -208,6 +211,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mPolicyVisibilityAfterAnim = true; private boolean mAppOpVisibility = true; boolean mPermanentlyHidden; // the window should never be shown again + // This is a non-system overlay window that is currently force hidden. + private boolean mForceHideNonSystemOverlayWindow; boolean mAppFreezing; boolean mHidden; // Used to determine if to show child windows. boolean mWallpaperVisible; // for wallpaper, what was last vis report? @@ -2370,6 +2375,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // to handle their windows being removed from under them. return false; } + if (mForceHideNonSystemOverlayWindow) { + // This is an alert window that is currently force hidden. + return false; + } if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { // Already showing. return false; @@ -2446,6 +2455,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return true; } + void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) { + if (mOwnerCanAddInternalSystemWindow + || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) { + return; + } + if (mForceHideNonSystemOverlayWindow == forceHide) { + return; + } + mForceHideNonSystemOverlayWindow = forceHide; + if (forceHide) { + hideLw(true /* doAnimation */, true /* requestAnim */); + } else { + showLw(true /* doAnimation */, true /* requestAnim */); + } + } + public void setAppOpVisibilityLw(boolean state) { if (mAppOpVisibility != state) { mAppOpVisibility = state; @@ -3327,7 +3352,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(Integer.toHexString(mSystemUiVisibility)); } if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility - || isParentWindowHidden()|| mPermanentlyHidden) { + || isParentWindowHidden()|| mPermanentlyHidden || mForceHideNonSystemOverlayWindow) { pw.print(prefix); pw.print("mPolicyVisibility="); pw.print(mPolicyVisibility); pw.print(" mPolicyVisibilityAfterAnim="); @@ -3335,7 +3360,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.print(" mAppOpVisibility="); pw.print(mAppOpVisibility); pw.print(" parentHidden="); pw.print(isParentWindowHidden()); - pw.print(" mPermanentlyHidden="); pw.println(mPermanentlyHidden); + pw.print(" mPermanentlyHidden="); pw.print(mPermanentlyHidden); + pw.print(" mForceHideNonSystemOverlayWindow="); pw.println( + mForceHideNonSystemOverlayWindow); } if (!mRelayoutCalled || mLayoutNeeded) { pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled); @@ -3590,6 +3617,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0; } + /** + * Returns true if any window added by an application process that if of type + * {@link android.view.WindowManager.LayoutParams#TYPE_TOAST} or that requires that requires + * {@link android.app.AppOpsManager#OP_SYSTEM_ALERT_WINDOW} permission should be hidden when + * this window is visible. + */ + boolean hideNonSystemOverlayWindowsWhenVisible() { + return (mAttrs.privateFlags & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0 + && mSession.mCanHideNonSystemOverlayWindows; + } + /** Returns the parent window if this is a child of another window, else null. */ WindowState getParentWindow() { // NOTE: We are not calling getParent() directly as the WindowState might be a child of a diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 27927e6c0693..1728cfbb6ef0 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -24,10 +24,8 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static android.view.Surface.SCALING_MODE_FREEZE; import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -514,6 +512,8 @@ class WindowSurfaceController { void setShown(boolean surfaceShown) { mSurfaceShown = surfaceShown; + mService.updateNonSystemOverlayWindowsVisibilityIfNeeded(mAnimator.mWin, surfaceShown); + if (mWindowSession != null) { mWindowSession.onWindowSurfaceVisibilityChanged(this, mSurfaceShown, mWindowType); } |