1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.classifier; 18 19 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD; 20 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER; 21 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; 22 import static com.android.systemui.classifier.Classifier.QS_SWIPE; 23 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; 24 25 import android.provider.DeviceConfig; 26 import android.view.MotionEvent; 27 28 import com.android.systemui.plugins.FalsingManager; 29 import com.android.systemui.util.DeviceConfigProxy; 30 31 import java.util.Locale; 32 33 import javax.inject.Inject; 34 35 36 /** 37 * False touch if proximity sensor is covered for more than a certain percentage of the gesture. 38 * 39 * This classifier is essentially a no-op for QUICK_SETTINGS, as we assume the sensor may be 40 * covered when swiping from the top. 41 */ 42 class ProximityClassifier extends FalsingClassifier { 43 44 private static final float PERCENT_COVERED_THRESHOLD = 0.1f; 45 private final DistanceClassifier mDistanceClassifier; 46 private final float mPercentCoveredThreshold; 47 48 private boolean mNear; 49 private long mGestureStartTimeNs; 50 private long mPrevNearTimeNs; 51 private long mNearDurationNs; 52 private float mPercentNear; 53 54 @Inject ProximityClassifier(DistanceClassifier distanceClassifier, FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy)55 ProximityClassifier(DistanceClassifier distanceClassifier, 56 FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { 57 super(dataProvider); 58 mDistanceClassifier = distanceClassifier; 59 60 mPercentCoveredThreshold = deviceConfigProxy.getFloat( 61 DeviceConfig.NAMESPACE_SYSTEMUI, 62 BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD, 63 PERCENT_COVERED_THRESHOLD); 64 } 65 66 @Override onSessionStarted()67 void onSessionStarted() { 68 mPrevNearTimeNs = 0; 69 mPercentNear = 0; 70 } 71 72 @Override onSessionEnded()73 void onSessionEnded() { 74 mPrevNearTimeNs = 0; 75 mPercentNear = 0; 76 } 77 78 @Override onTouchEvent(MotionEvent motionEvent)79 public void onTouchEvent(MotionEvent motionEvent) { 80 int action = motionEvent.getActionMasked(); 81 82 if (action == MotionEvent.ACTION_DOWN) { 83 mGestureStartTimeNs = motionEvent.getEventTimeNano(); 84 if (mPrevNearTimeNs > 0) { 85 // We only care about if the proximity sensor is triggered while a move event is 86 // happening. 87 mPrevNearTimeNs = motionEvent.getEventTimeNano(); 88 } 89 logDebug("Gesture start time: " + mGestureStartTimeNs); 90 mNearDurationNs = 0; 91 } 92 93 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { 94 update(mNear, motionEvent.getEventTimeNano()); 95 long duration = motionEvent.getEventTimeNano() - mGestureStartTimeNs; 96 97 logDebug("Gesture duration, Proximity duration: " + duration + ", " + mNearDurationNs); 98 99 if (duration == 0) { 100 mPercentNear = mNear ? 1.0f : 0.0f; 101 } else { 102 mPercentNear = (float) mNearDurationNs / (float) duration; 103 } 104 } 105 106 } 107 108 @Override onProximityEvent( FalsingManager.ProximityEvent proximityEvent)109 public void onProximityEvent( 110 FalsingManager.ProximityEvent proximityEvent) { 111 boolean covered = proximityEvent.getCovered(); 112 long timestampNs = proximityEvent.getTimestampNs(); 113 logDebug("Sensor is: " + covered + " at time " + timestampNs); 114 update(covered, timestampNs); 115 } 116 117 @Override calculateFalsingResult( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)118 Result calculateFalsingResult( 119 @Classifier.InteractionType int interactionType, 120 double historyBelief, double historyConfidence) { 121 if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER 122 || interactionType == QS_COLLAPSE || interactionType == QS_SWIPE) { 123 return Result.passed(0); 124 } 125 126 if (mPercentNear > mPercentCoveredThreshold) { 127 Result longSwipeResult = mDistanceClassifier.isLongSwipe(); 128 return longSwipeResult.isFalse() 129 ? falsed( 130 0.5, getReason(longSwipeResult, mPercentNear, mPercentCoveredThreshold)) 131 : Result.passed(0.5); 132 } 133 134 return Result.passed(0.5); 135 } 136 getReason(Result longSwipeResult, float percentNear, float percentCoveredThreshold)137 private static String getReason(Result longSwipeResult, float percentNear, 138 float percentCoveredThreshold) { 139 return String.format( 140 (Locale) null, 141 "{percentInProximity=%f, threshold=%f, distanceClassifier=%s}", 142 percentNear, 143 percentCoveredThreshold, 144 longSwipeResult.getReason()); 145 } 146 147 /** 148 * @param near is the sensor showing the near state right now 149 * @param timeStampNs time of this event in nanoseconds 150 */ update(boolean near, long timeStampNs)151 private void update(boolean near, long timeStampNs) { 152 if (mPrevNearTimeNs != 0 && timeStampNs > mPrevNearTimeNs && mNear) { 153 mNearDurationNs += timeStampNs - mPrevNearTimeNs; 154 logDebug("Updating duration: " + mNearDurationNs); 155 } 156 157 if (near) { 158 logDebug("Set prevNearTimeNs: " + timeStampNs); 159 mPrevNearTimeNs = timeStampNs; 160 } 161 162 mNear = near; 163 } 164 } 165