diff options
4 files changed, 307 insertions, 12 deletions
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 134388f6bca2..fa30f49ef7db 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -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> diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 5f1b042636e4..661b347608ed 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 1d890d0dde6c..9b3ed33e3158 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -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); diff --git a/packages/SystemUI/tests/src/com/android/systemui/phone/DozeParametersTests.java b/packages/SystemUI/tests/src/com/android/systemui/phone/DozeParametersTests.java new file mode 100644 index 000000000000..07334f3b7d25 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/phone/DozeParametersTests.java @@ -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 + } + } + +}
\ No newline at end of file |