Merge "Doze: Add config for fine grained proximity checks" into nyc-mr1-dev
This commit is contained in:
@ -224,9 +224,25 @@
|
||||
<!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations -->
|
||||
<integer name="doze_pickup_vibration_threshold">2000</integer>
|
||||
|
||||
<!-- Doze: can we assume the pickup sensor includes a proximity check? -->
|
||||
<!-- Doze: can we assume the pickup sensor includes a proximity check?
|
||||
This is ignored if doze_pickup_subtype_performs_proximity_check is not empty.
|
||||
@deprecated: use doze_pickup_subtype_performs_proximity_check instead.-->
|
||||
<bool name="doze_pickup_performs_proximity_check">false</bool>
|
||||
|
||||
<!-- Doze: a list of pickup sensor subtypes that perform a proximity check before they trigger.
|
||||
If not empty, either * or !* must appear to specify the default.
|
||||
If empty, falls back to doze_pickup_performs_proximity_check.
|
||||
|
||||
Examples: 1,2,3,!* -> subtypes 1,2 and 3 perform the check, all others don't.
|
||||
!1,!2,* -> subtypes 1 and 2 don't perform the check, all others do.
|
||||
!8,* -> subtype 8 does not perform the check, all others do
|
||||
1,1,* -> illegal, every item may only appear once
|
||||
1,!1,* -> illegal, no contradictions allowed
|
||||
1,2 -> illegal, need either * or !*
|
||||
1,,4a3 -> illegal, no empty or non-numeric terms allowed
|
||||
-->
|
||||
<string name="doze_pickup_subtype_performs_proximity_check"></string>
|
||||
|
||||
<!-- Doze: pulse parameter - how long does it take to fade in? -->
|
||||
<integer name="doze_pulse_duration_in">900</integer>
|
||||
|
||||
|
@ -214,6 +214,10 @@ public class DozeService extends DreamService {
|
||||
}
|
||||
|
||||
private void requestPulse(final int reason) {
|
||||
requestPulse(reason, false /* performedProxCheck */);
|
||||
}
|
||||
|
||||
private void requestPulse(final int reason, boolean performedProxCheck) {
|
||||
if (mHost != null && mDreaming && !mPulsing) {
|
||||
// Let the host know we want to pulse. Wait for it to be ready, then
|
||||
// turn the screen on. When finished, turn the screen off again.
|
||||
@ -226,10 +230,9 @@ public class DozeService extends DreamService {
|
||||
return;
|
||||
}
|
||||
final long start = SystemClock.uptimeMillis();
|
||||
final boolean nonBlocking = reason == DozeLog.PULSE_REASON_SENSOR_PICKUP
|
||||
&& mDozeParameters.getPickupPerformsProxCheck();
|
||||
if (nonBlocking) {
|
||||
// proximity check is only done to capture statistics, continue pulsing
|
||||
if (performedProxCheck) {
|
||||
// the caller already performed a successful proximity check; we'll only do one to
|
||||
// capture statistics, continue pulsing immediately.
|
||||
continuePulsing(reason);
|
||||
}
|
||||
// perform a proximity check
|
||||
@ -239,7 +242,7 @@ public class DozeService extends DreamService {
|
||||
final boolean isNear = result == RESULT_NEAR;
|
||||
final long end = SystemClock.uptimeMillis();
|
||||
DozeLog.traceProximityResult(mContext, isNear, end - start, reason);
|
||||
if (nonBlocking) {
|
||||
if (performedProxCheck) {
|
||||
// we already continued
|
||||
return;
|
||||
}
|
||||
@ -540,9 +543,12 @@ public class DozeService extends DreamService {
|
||||
mWakeLock.acquire();
|
||||
try {
|
||||
if (DEBUG) Log.d(mTag, "onTrigger: " + triggerEventToString(event));
|
||||
boolean sensorPerformsProxCheck = false;
|
||||
if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
|
||||
int subType = (int) event.values[0];
|
||||
MetricsLogger.action(mContext, MetricsEvent.ACTION_AMBIENT_GESTURE, subType);
|
||||
sensorPerformsProxCheck = mDozeParameters.getPickupSubtypePerformsProxCheck(
|
||||
subType);
|
||||
}
|
||||
if (mDebugVibrate) {
|
||||
final Vibrator v = (Vibrator) mContext.getSystemService(
|
||||
@ -555,7 +561,7 @@ public class DozeService extends DreamService {
|
||||
}
|
||||
|
||||
mRegistered = false;
|
||||
requestPulse(mPulseReason);
|
||||
requestPulse(mPulseReason, sensorPerformsProxCheck);
|
||||
updateListener(); // reregister, this sensor only fires once
|
||||
|
||||
// reset the notification pulse schedule, but only if we think we were not triggered
|
||||
|
@ -21,6 +21,7 @@ import android.os.SystemProperties;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
@ -39,6 +40,8 @@ public class DozeParameters {
|
||||
|
||||
private static PulseSchedule sPulseSchedule;
|
||||
|
||||
private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
|
||||
|
||||
public DozeParameters(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
@ -61,7 +64,20 @@ public class DozeParameters {
|
||||
pw.print(" getPulseSchedule(): "); pw.println(getPulseSchedule());
|
||||
pw.print(" getPulseScheduleResets(): "); pw.println(getPulseScheduleResets());
|
||||
pw.print(" getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
|
||||
pw.print(" getPickupPerformsProxCheck(): "); pw.println(getPickupPerformsProxCheck());
|
||||
pw.print(" getPickupSubtypePerformsProxCheck(): ");pw.println(
|
||||
dumpPickupSubtypePerformsProxCheck());
|
||||
}
|
||||
|
||||
private String dumpPickupSubtypePerformsProxCheck() {
|
||||
// Refresh sPickupSubtypePerformsProxMatcher
|
||||
getPickupSubtypePerformsProxCheck(0);
|
||||
|
||||
if (sPickupSubtypePerformsProxMatcher == null) {
|
||||
return "fallback: " + mContext.getResources().getBoolean(
|
||||
R.bool.doze_pickup_performs_proximity_check);
|
||||
} else {
|
||||
return "spec: " + sPickupSubtypePerformsProxMatcher.mSpec;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getDisplayStateSupported() {
|
||||
@ -106,10 +122,6 @@ public class DozeParameters {
|
||||
return getBoolean("doze.pulse.proxcheck", R.bool.doze_proximity_check_before_pulse);
|
||||
}
|
||||
|
||||
public boolean getPickupPerformsProxCheck() {
|
||||
return getBoolean("doze.pickup.proxcheck", R.bool.doze_pickup_performs_proximity_check);
|
||||
}
|
||||
|
||||
public boolean getPulseOnNotifications() {
|
||||
return getBoolean("doze.pulse.notifications", R.bool.doze_pulse_on_notifications);
|
||||
}
|
||||
@ -143,6 +155,101 @@ public class DozeParameters {
|
||||
return SystemProperties.get(propName, mContext.getString(resId));
|
||||
}
|
||||
|
||||
public boolean getPickupSubtypePerformsProxCheck(int subType) {
|
||||
String spec = getString("doze.pickup.proxcheck",
|
||||
R.string.doze_pickup_subtype_performs_proximity_check);
|
||||
|
||||
if (TextUtils.isEmpty(spec)) {
|
||||
// Fall back to non-subtype based property.
|
||||
return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check);
|
||||
}
|
||||
|
||||
if (sPickupSubtypePerformsProxMatcher == null
|
||||
|| !TextUtils.equals(spec, sPickupSubtypePerformsProxMatcher.mSpec)) {
|
||||
sPickupSubtypePerformsProxMatcher = new IntInOutMatcher(spec);
|
||||
}
|
||||
|
||||
return sPickupSubtypePerformsProxMatcher.isIn(subType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
|
||||
* listed, will not match numbers that are listed with a ! prefix, and will match / not match
|
||||
* unlisted numbers depending on whether * or !* is present.
|
||||
*
|
||||
* * -> match any numbers that are not explicitly listed
|
||||
* !* -> don't match any numbers that are not explicitly listed
|
||||
* 2 -> match 2
|
||||
* !3 -> don't match 3
|
||||
*
|
||||
* It is illegal to specify:
|
||||
* - an empty spec
|
||||
* - a spec containing that are empty, or a lone !
|
||||
* - a spec for anything other than numbers or *
|
||||
* - multiple terms for the same number / multiple *s
|
||||
*/
|
||||
public static class IntInOutMatcher {
|
||||
private static final String WILDCARD = "*";
|
||||
private static final char OUT_PREFIX = '!';
|
||||
|
||||
private final SparseBooleanArray mIsIn;
|
||||
private final boolean mDefaultIsIn;
|
||||
final String mSpec;
|
||||
|
||||
public IntInOutMatcher(String spec) {
|
||||
if (TextUtils.isEmpty(spec)) {
|
||||
throw new IllegalArgumentException("Spec must not be empty");
|
||||
}
|
||||
|
||||
boolean defaultIsIn = false;
|
||||
boolean foundWildcard = false;
|
||||
|
||||
mSpec = spec;
|
||||
mIsIn = new SparseBooleanArray();
|
||||
|
||||
for (String itemPrefixed : spec.split(",", -1)) {
|
||||
if (itemPrefixed.length() == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal spec, must not have zero-length items: `" + spec + "`");
|
||||
}
|
||||
boolean isIn = itemPrefixed.charAt(0) != OUT_PREFIX;
|
||||
String item = isIn ? itemPrefixed : itemPrefixed.substring(1);
|
||||
|
||||
if (itemPrefixed.length() == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal spec, must not have zero-length items: `" + spec + "`");
|
||||
}
|
||||
|
||||
if (WILDCARD.equals(item)) {
|
||||
if (foundWildcard) {
|
||||
throw new IllegalArgumentException("Illegal spec, `" + WILDCARD +
|
||||
"` must not appear multiple times in `" + spec + "`");
|
||||
}
|
||||
defaultIsIn = isIn;
|
||||
foundWildcard = true;
|
||||
} else {
|
||||
int key = Integer.parseInt(item);
|
||||
if (mIsIn.indexOfKey(key) >= 0) {
|
||||
throw new IllegalArgumentException("Illegal spec, `" + key +
|
||||
"` must not appear multiple times in `" + spec + "`");
|
||||
}
|
||||
mIsIn.put(key, isIn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundWildcard) {
|
||||
throw new IllegalArgumentException("Illegal spec, must specify either * or !*");
|
||||
}
|
||||
|
||||
mDefaultIsIn = defaultIsIn;
|
||||
}
|
||||
|
||||
public boolean isIn(int value) {
|
||||
return (mIsIn.get(value, mDefaultIsIn));
|
||||
}
|
||||
}
|
||||
|
||||
public static class PulseSchedule {
|
||||
private static final Pattern PATTERN = Pattern.compile("(\\d+?)s", 0);
|
||||
|
||||
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.phone;
|
||||
|
||||
import com.android.systemui.statusbar.phone.DozeParameters.IntInOutMatcher;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
@SmallTest
|
||||
public class DozeParametersTests extends AndroidTestCase {
|
||||
|
||||
public void test_inOutMatcher_defaultIn() {
|
||||
IntInOutMatcher intInOutMatcher = new IntInOutMatcher("*");
|
||||
|
||||
assertTrue(intInOutMatcher.isIn(1));
|
||||
assertTrue(intInOutMatcher.isIn(-1));
|
||||
assertTrue(intInOutMatcher.isIn(0));
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_defaultOut() {
|
||||
IntInOutMatcher intInOutMatcher = new IntInOutMatcher("!*");
|
||||
|
||||
assertFalse(intInOutMatcher.isIn(1));
|
||||
assertFalse(intInOutMatcher.isIn(-1));
|
||||
assertFalse(intInOutMatcher.isIn(0));
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_someIn() {
|
||||
IntInOutMatcher intInOutMatcher = new IntInOutMatcher("1,2,3,!*");
|
||||
|
||||
assertTrue(intInOutMatcher.isIn(1));
|
||||
assertTrue(intInOutMatcher.isIn(2));
|
||||
assertTrue(intInOutMatcher.isIn(3));
|
||||
|
||||
assertFalse(intInOutMatcher.isIn(0));
|
||||
assertFalse(intInOutMatcher.isIn(4));
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_someOut() {
|
||||
IntInOutMatcher intInOutMatcher = new IntInOutMatcher("!1,!2,!3,*");
|
||||
|
||||
assertFalse(intInOutMatcher.isIn(1));
|
||||
assertFalse(intInOutMatcher.isIn(2));
|
||||
assertFalse(intInOutMatcher.isIn(3));
|
||||
|
||||
assertTrue(intInOutMatcher.isIn(0));
|
||||
assertTrue(intInOutMatcher.isIn(4));
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_mixed() {
|
||||
IntInOutMatcher intInOutMatcher = new IntInOutMatcher("!1,2,!3,*");
|
||||
|
||||
assertFalse(intInOutMatcher.isIn(1));
|
||||
assertTrue(intInOutMatcher.isIn(2));
|
||||
assertFalse(intInOutMatcher.isIn(3));
|
||||
|
||||
assertTrue(intInOutMatcher.isIn(0));
|
||||
assertTrue(intInOutMatcher.isIn(4));
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failEmpty() {
|
||||
try {
|
||||
new IntInOutMatcher("");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failNull() {
|
||||
try {
|
||||
new IntInOutMatcher(null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failEmptyClause() {
|
||||
try {
|
||||
new IntInOutMatcher("!1,*,");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failDuplicate() {
|
||||
try {
|
||||
new IntInOutMatcher("!1,*,!1");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failDuplicateDefault() {
|
||||
try {
|
||||
new IntInOutMatcher("!1,*,*");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failMalformedNot() {
|
||||
try {
|
||||
new IntInOutMatcher("!,*");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failText() {
|
||||
try {
|
||||
new IntInOutMatcher("!abc,*");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failContradiction() {
|
||||
try {
|
||||
new IntInOutMatcher("1,!1,*");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failContradictionDefault() {
|
||||
try {
|
||||
new IntInOutMatcher("1,*,!*");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void test_inOutMatcher_failMissingDefault() {
|
||||
try {
|
||||
new IntInOutMatcher("1");
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user