Remove old falsing implementation.
This code has not been used since Android P. Bug: 172655679 Test: manual && atest SystemUITests Change-Id: I2b96af477b063bcbb68c48951121258e56bbfff2
This commit is contained in:
@@ -21,6 +21,7 @@ import android.view.MotionEvent;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
@@ -139,7 +140,8 @@ public interface FalsingManager {
|
||||
|
||||
void onTouchEvent(MotionEvent ev, int width, int height);
|
||||
|
||||
void dump(PrintWriter pw);
|
||||
/** From com.android.systemui.Dumpable. */
|
||||
void dump(FileDescriptor fd, PrintWriter pw, String[] args);
|
||||
|
||||
void cleanup();
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the speed and distance between successive points of a Stroke.
|
||||
* It looks at two consecutive speeds between two points and calculates the ratio between them.
|
||||
* The final result is the maximum of these values. It does the same for distances. If some speed
|
||||
* or distance is equal to zero then the ratio between this and the next part is not calculated. To
|
||||
* the duration of each part there is added one nanosecond so that it is always possible to
|
||||
* calculate the speed of a part.
|
||||
*/
|
||||
public class AccelerationClassifier extends StrokeClassifier {
|
||||
private final HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
|
||||
|
||||
public AccelerationClassifier(ClassifierData classifierData) {
|
||||
mClassifierData = classifierData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "ACC";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
int action = event.getActionMasked();
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mStrokeMap.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
|
||||
Point point = stroke.getPoints().get(stroke.getPoints().size() - 1);
|
||||
if (mStrokeMap.get(stroke) == null) {
|
||||
mStrokeMap.put(stroke, new Data(point));
|
||||
} else {
|
||||
mStrokeMap.get(stroke).addPoint(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
Data data = mStrokeMap.get(stroke);
|
||||
return 2 * SpeedRatioEvaluator.evaluate(data.maxSpeedRatio);
|
||||
}
|
||||
|
||||
private static class Data {
|
||||
|
||||
static final float MILLIS_TO_NANOS = 1e6f;
|
||||
|
||||
Point previousPoint;
|
||||
float previousSpeed = 0;
|
||||
float maxSpeedRatio = 0;
|
||||
|
||||
public Data(Point point) {
|
||||
previousPoint = point;
|
||||
}
|
||||
|
||||
public void addPoint(Point point) {
|
||||
float distance = previousPoint.dist(point);
|
||||
float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1);
|
||||
float speed = distance / duration;
|
||||
|
||||
if (duration > 20 * MILLIS_TO_NANOS || duration < 5 * MILLIS_TO_NANOS) {
|
||||
// reject this segment and ensure we won't use data about it in the next round.
|
||||
previousSpeed = 0;
|
||||
previousPoint = point;
|
||||
return;
|
||||
}
|
||||
if (previousSpeed != 0.0f) {
|
||||
maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed);
|
||||
}
|
||||
|
||||
previousSpeed = speed;
|
||||
previousPoint = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.SystemProperties;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A classifier which calculates the variance of differences between successive angles in a stroke.
|
||||
* For each stroke it keeps its last three points. If some successive points are the same, it
|
||||
* ignores the repetitions. If a new point is added, the classifier calculates the angle between
|
||||
* the last three points. After that, it calculates the difference between this angle and the
|
||||
* previously calculated angle. Then it calculates the variance of the differences from a stroke.
|
||||
* To the differences there is artificially added value 0.0 and the difference between the first
|
||||
* angle and PI (angles are in radians). It helps with strokes which have few points and punishes
|
||||
* more strokes which are not smooth.
|
||||
*
|
||||
* This classifier also tries to split the stroke into two parts in the place in which the biggest
|
||||
* angle is. It calculates the angle variance of the two parts and sums them up. The reason the
|
||||
* classifier is doing this, is because some human swipes at the beginning go for a moment in one
|
||||
* direction and then they rapidly change direction for the rest of the stroke (like a tick). The
|
||||
* final result is the minimum of angle variance of the whole stroke and the sum of angle variances
|
||||
* of the two parts split up. The classifier tries the tick option only if the first part is
|
||||
* shorter than the second part.
|
||||
*
|
||||
* Additionally, the classifier classifies the angles as left angles (those angles which value is
|
||||
* in [0.0, PI - ANGLE_DEVIATION) interval), straight angles
|
||||
* ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles
|
||||
* ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are
|
||||
* in the same direction (straight angles can be left angels or right angles)
|
||||
*/
|
||||
public class AnglesClassifier extends StrokeClassifier {
|
||||
private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
|
||||
|
||||
public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.ang",
|
||||
Build.IS_DEBUGGABLE);
|
||||
|
||||
private static String TAG = "ANG";
|
||||
|
||||
public AnglesClassifier(ClassifierData classifierData) {
|
||||
mClassifierData = classifierData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
int action = event.getActionMasked();
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mStrokeMap.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
|
||||
|
||||
if (mStrokeMap.get(stroke) == null) {
|
||||
mStrokeMap.put(stroke, new Data());
|
||||
}
|
||||
mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
Data data = mStrokeMap.get(stroke);
|
||||
return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance(), type)
|
||||
+ AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage(), type);
|
||||
}
|
||||
|
||||
private static class Data {
|
||||
private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f;
|
||||
|
||||
private List<Point> mLastThreePoints = new ArrayList<>();
|
||||
private float mFirstAngleVariance;
|
||||
private float mPreviousAngle;
|
||||
private float mBiggestAngle;
|
||||
private float mSumSquares;
|
||||
private float mSecondSumSquares;
|
||||
private float mSum;
|
||||
private float mSecondSum;
|
||||
private float mCount;
|
||||
private float mSecondCount;
|
||||
private float mFirstLength;
|
||||
private float mLength;
|
||||
private float mAnglesCount;
|
||||
private float mLeftAngles;
|
||||
private float mRightAngles;
|
||||
private float mStraightAngles;
|
||||
|
||||
public Data() {
|
||||
mFirstAngleVariance = 0.0f;
|
||||
mPreviousAngle = (float) Math.PI;
|
||||
mBiggestAngle = 0.0f;
|
||||
mSumSquares = mSecondSumSquares = 0.0f;
|
||||
mSum = mSecondSum = 0.0f;
|
||||
mCount = mSecondCount = 1.0f;
|
||||
mLength = mFirstLength = 0.0f;
|
||||
mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f;
|
||||
}
|
||||
|
||||
public void addPoint(Point point) {
|
||||
// Checking if the added point is different than the previously added point
|
||||
// Repetitions are being ignored so that proper angles are calculated.
|
||||
if (mLastThreePoints.isEmpty()
|
||||
|| !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) {
|
||||
if (!mLastThreePoints.isEmpty()) {
|
||||
mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point);
|
||||
}
|
||||
mLastThreePoints.add(point);
|
||||
if (mLastThreePoints.size() == 4) {
|
||||
mLastThreePoints.remove(0);
|
||||
|
||||
float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
|
||||
mLastThreePoints.get(2));
|
||||
|
||||
mAnglesCount++;
|
||||
if (angle < Math.PI - ANGLE_DEVIATION) {
|
||||
mLeftAngles++;
|
||||
} else if (angle <= Math.PI + ANGLE_DEVIATION) {
|
||||
mStraightAngles++;
|
||||
} else {
|
||||
mRightAngles++;
|
||||
}
|
||||
|
||||
float difference = angle - mPreviousAngle;
|
||||
|
||||
// If this is the biggest angle of the stroke so then we save the value of
|
||||
// the angle variance so far and start to count the values for the angle
|
||||
// variance of the second part.
|
||||
if (mBiggestAngle < angle) {
|
||||
mBiggestAngle = angle;
|
||||
mFirstLength = mLength;
|
||||
mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount);
|
||||
mSecondSumSquares = 0.0f;
|
||||
mSecondSum = 0.0f;
|
||||
mSecondCount = 1.0f;
|
||||
} else {
|
||||
mSecondSum += difference;
|
||||
mSecondSumSquares += difference * difference;
|
||||
mSecondCount += 1.0;
|
||||
}
|
||||
|
||||
mSum += difference;
|
||||
mSumSquares += difference * difference;
|
||||
mCount += 1.0;
|
||||
mPreviousAngle = angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float getAnglesVariance(float sumSquares, float sum, float count) {
|
||||
return sumSquares / count - (sum / count) * (sum / count);
|
||||
}
|
||||
|
||||
public float getAnglesVariance() {
|
||||
float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount);
|
||||
if (VERBOSE) {
|
||||
FalsingLog.i(TAG, "getAnglesVariance: (first pass) " + anglesVariance);
|
||||
FalsingLog.i(TAG, " - mFirstLength=" + mFirstLength);
|
||||
FalsingLog.i(TAG, " - mLength=" + mLength);
|
||||
}
|
||||
if (mFirstLength < mLength / 2f) {
|
||||
anglesVariance = Math.min(anglesVariance, mFirstAngleVariance
|
||||
+ getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount));
|
||||
if (VERBOSE) FalsingLog.i(TAG, "getAnglesVariance: (second pass) " + anglesVariance);
|
||||
}
|
||||
return anglesVariance;
|
||||
}
|
||||
|
||||
public float getAnglesPercentage() {
|
||||
if (mAnglesCount == 0.0f) {
|
||||
if (VERBOSE) FalsingLog.i(TAG, "getAnglesPercentage: count==0, result=1");
|
||||
return 1.0f;
|
||||
}
|
||||
final float result = (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount;
|
||||
if (VERBOSE) {
|
||||
FalsingLog.i(TAG, "getAnglesPercentage: left=" + mLeftAngles + " right="
|
||||
+ mRightAngles + " straight=" + mStraightAngles + " count=" + mAnglesCount
|
||||
+ " result=" + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class AnglesPercentageEvaluator {
|
||||
public static float evaluate(float value, int type) {
|
||||
final boolean secureUnlock = type == Classifier.BOUNCER_UNLOCK;
|
||||
float evaluation = 0.0f;
|
||||
if (value < 1.00 && !secureUnlock) evaluation++;
|
||||
if (value < 0.90 && !secureUnlock) evaluation++;
|
||||
if (value < 0.70) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class AnglesVarianceEvaluator {
|
||||
public static float evaluate(float value, int type) {
|
||||
float evaluation = 0.0f;
|
||||
if (value > 0.20) evaluation++;
|
||||
if (value > 0.40) evaluation++;
|
||||
if (value > 0.80) evaluation++;
|
||||
if (value > 1.50) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,6 @@ public abstract class Classifier {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface InteractionType {}
|
||||
|
||||
/**
|
||||
* Contains all the information about touch events from which the classifier can query
|
||||
*/
|
||||
protected ClassifierData mClassifierData;
|
||||
|
||||
/**
|
||||
* Informs the classifier that a new touch event has occurred
|
||||
*/
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Contains data which is used to classify interaction sequences on the lockscreen. It does, for
|
||||
* example, provide information on the current touch state.
|
||||
*/
|
||||
public class ClassifierData {
|
||||
private static final long MINIMUM_DT_NANOS = 16666666; // 60Hz
|
||||
private static final long MINIMUM_DT_SMEAR_NANOS = 2500000; // 2.5ms
|
||||
|
||||
private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>();
|
||||
private ArrayList<Stroke> mEndingStrokes = new ArrayList<>();
|
||||
private final float mDpi;
|
||||
|
||||
public ClassifierData(float dpi) {
|
||||
mDpi = dpi;
|
||||
}
|
||||
|
||||
/** Returns true if the event should be considered, false otherwise. */
|
||||
public boolean update(MotionEvent event) {
|
||||
// We limit to 60hz sampling. Drop anything happening faster than that.
|
||||
// Legacy code was created with an assumed sampling rate. As devices increase their
|
||||
// sampling rate, this creates potentialy false positives.
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_MOVE
|
||||
&& mCurrentStrokes.size() != 0
|
||||
&& event.getEventTimeNano() - mCurrentStrokes.valueAt(0).getLastEventTimeNano()
|
||||
< MINIMUM_DT_NANOS - MINIMUM_DT_SMEAR_NANOS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mEndingStrokes.clear();
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mCurrentStrokes.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
int id = event.getPointerId(i);
|
||||
if (mCurrentStrokes.get(id) == null) {
|
||||
mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mDpi));
|
||||
}
|
||||
mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i),
|
||||
event.getEventTimeNano());
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
|
||||
|| (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
|
||||
mEndingStrokes.add(getStroke(id));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void cleanUp(MotionEvent event) {
|
||||
mEndingStrokes.clear();
|
||||
int action = event.getActionMasked();
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
int id = event.getPointerId(i);
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
|
||||
|| (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
|
||||
mCurrentStrokes.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of Strokes which are ending in the recently added MotionEvent
|
||||
*/
|
||||
public ArrayList<Stroke> getEndingStrokes() {
|
||||
return mEndingStrokes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id the id from MotionEvent
|
||||
* @return the Stroke assigned to the id
|
||||
*/
|
||||
public Stroke getStroke(int id) {
|
||||
return mCurrentStrokes.get(id);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the general direction of a stroke and evaluates it depending on
|
||||
* the type of action that takes place.
|
||||
*/
|
||||
public class DirectionClassifier extends StrokeClassifier {
|
||||
public DirectionClassifier(ClassifierData classifierData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "DIR";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
Point firstPoint = stroke.getPoints().get(0);
|
||||
Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
|
||||
return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y,
|
||||
type);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class DirectionEvaluator {
|
||||
public static float evaluate(float xDiff, float yDiff, int type) {
|
||||
float falsingEvaluation = 5.5f;
|
||||
boolean vertical = Math.abs(yDiff) >= Math.abs(xDiff);
|
||||
switch (type) {
|
||||
case Classifier.QUICK_SETTINGS:
|
||||
case Classifier.PULSE_EXPAND:
|
||||
case Classifier.NOTIFICATION_DRAG_DOWN:
|
||||
if (!vertical || yDiff <= 0.0) {
|
||||
return falsingEvaluation;
|
||||
}
|
||||
break;
|
||||
case Classifier.NOTIFICATION_DISMISS:
|
||||
if (vertical) {
|
||||
return falsingEvaluation;
|
||||
}
|
||||
break;
|
||||
case Classifier.UNLOCK:
|
||||
case Classifier.BOUNCER_UNLOCK:
|
||||
if (!vertical || yDiff >= 0.0) {
|
||||
return falsingEvaluation;
|
||||
}
|
||||
break;
|
||||
case Classifier.LEFT_AFFORDANCE:
|
||||
if (xDiff < 0.0 && yDiff > 0.0) {
|
||||
return falsingEvaluation;
|
||||
}
|
||||
break;
|
||||
case Classifier.RIGHT_AFFORDANCE:
|
||||
if (xDiff > 0.0 && yDiff > 0.0) {
|
||||
return falsingEvaluation;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the ratio between the duration of the stroke and its number of
|
||||
* points.
|
||||
*/
|
||||
public class DurationCountClassifier extends StrokeClassifier {
|
||||
public DurationCountClassifier(ClassifierData classifierData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "DUR";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
|
||||
public class DurationCountEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value < 0.0105) evaluation++;
|
||||
if (value < 0.00909) evaluation++;
|
||||
if (value < 0.00667) evaluation++;
|
||||
if (value > 0.0333) evaluation++;
|
||||
if (value > 0.0500) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the distance between the first and the last point from the stroke.
|
||||
*/
|
||||
public class EndPointLengthClassifier extends StrokeClassifier {
|
||||
public EndPointLengthClassifier(ClassifierData classifierData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "END_LNGTH";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class EndPointLengthEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value < 0.05) evaluation += 2.0;
|
||||
if (value < 0.1) evaluation += 2.0;
|
||||
if (value < 0.2) evaluation += 2.0;
|
||||
if (value < 0.3) evaluation += 2.0;
|
||||
if (value < 0.4) evaluation += 2.0;
|
||||
if (value < 0.5) evaluation += 2.0;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the ratio between the total length covered by the stroke and the
|
||||
* distance between the first and last point from this stroke.
|
||||
*/
|
||||
public class EndPointRatioClassifier extends StrokeClassifier {
|
||||
public EndPointRatioClassifier(ClassifierData classifierData) {
|
||||
mClassifierData = classifierData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "END_RTIO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
float ratio;
|
||||
if (stroke.getTotalLength() == 0.0f) {
|
||||
ratio = 1.0f;
|
||||
} else {
|
||||
ratio = stroke.getEndPointLength() / stroke.getTotalLength();
|
||||
}
|
||||
return EndPointRatioEvaluator.evaluate(ratio);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class EndPointRatioEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value < 0.85) evaluation++;
|
||||
if (value < 0.75) evaluation++;
|
||||
if (value < 0.65) evaluation++;
|
||||
if (value < 0.55) evaluation++;
|
||||
if (value < 0.45) evaluation++;
|
||||
if (value < 0.35) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* 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.classifier;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.app.Application;
|
||||
import android.os.Build;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Keeps track of interesting falsing data.
|
||||
*
|
||||
* By default the log only gets collected on userdebug builds. To turn it on on user:
|
||||
* adb shell setprop debug.falsing_log true
|
||||
*
|
||||
* The log gets dumped as part of the SystemUI services. To dump on demand:
|
||||
* adb shell dumpsys activity service com.android.systemui StatusBar | grep -A 999 FALSING | less
|
||||
*
|
||||
* To dump into logcat:
|
||||
* adb shell setprop debug.falsing_logcat true
|
||||
*
|
||||
* To adjust the log buffer size:
|
||||
* adb shell setprop debug.falsing_log_size 200
|
||||
*/
|
||||
public class FalsingLog {
|
||||
public static final boolean ENABLED = SystemProperties.getBoolean("debug.falsing_log",
|
||||
Build.IS_DEBUGGABLE);
|
||||
private static final boolean LOGCAT = SystemProperties.getBoolean("debug.falsing_logcat",
|
||||
false);
|
||||
|
||||
public static final boolean VERBOSE = false;
|
||||
|
||||
private static final int MAX_SIZE = SystemProperties.getInt("debug.falsing_log_size", 100);
|
||||
|
||||
private static final String TAG = "FalsingLog";
|
||||
|
||||
private final ArrayDeque<String> mLog = new ArrayDeque<>(MAX_SIZE);
|
||||
private final SimpleDateFormat mFormat = new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
|
||||
|
||||
private static FalsingLog sInstance;
|
||||
|
||||
private FalsingLog() {
|
||||
}
|
||||
|
||||
public static void v(String tag, String s) {
|
||||
if (!VERBOSE) {
|
||||
return;
|
||||
}
|
||||
if (LOGCAT) {
|
||||
Log.v(TAG, tag + "\t" + s);
|
||||
}
|
||||
log("V", tag, s);
|
||||
}
|
||||
|
||||
public static void i(String tag, String s) {
|
||||
if (LOGCAT) {
|
||||
Log.i(TAG, tag + "\t" + s);
|
||||
}
|
||||
log("I", tag, s);
|
||||
}
|
||||
|
||||
public static void wLogcat(String tag, String s) {
|
||||
Log.w(TAG, tag + "\t" + s);
|
||||
log("W", tag, s);
|
||||
}
|
||||
|
||||
public static void w(String tag, String s) {
|
||||
if (LOGCAT) {
|
||||
Log.w(TAG, tag + "\t" + s);
|
||||
}
|
||||
log("W", tag, s);
|
||||
}
|
||||
|
||||
public static void e(String tag, String s) {
|
||||
if (LOGCAT) {
|
||||
Log.e(TAG, tag + "\t" + s);
|
||||
}
|
||||
log("E", tag, s);
|
||||
}
|
||||
|
||||
public static synchronized void log(String level, String tag, String s) {
|
||||
if (!ENABLED) {
|
||||
return;
|
||||
}
|
||||
if (sInstance == null) {
|
||||
sInstance = new FalsingLog();
|
||||
}
|
||||
|
||||
if (sInstance.mLog.size() >= MAX_SIZE) {
|
||||
sInstance.mLog.removeFirst();
|
||||
}
|
||||
String entry = new StringBuilder().append(sInstance.mFormat.format(new Date()))
|
||||
.append(" ").append(level).append(" ")
|
||||
.append(tag).append(" ").append(s).toString();
|
||||
sInstance.mLog.add(entry);
|
||||
}
|
||||
|
||||
public static synchronized void dump(PrintWriter pw) {
|
||||
pw.println("FALSING LOG:");
|
||||
if (!ENABLED) {
|
||||
pw.println("Disabled, to enable: setprop debug.falsing_log 1");
|
||||
pw.println();
|
||||
return;
|
||||
}
|
||||
if (sInstance == null || sInstance.mLog.isEmpty()) {
|
||||
pw.println("<empty>");
|
||||
pw.println();
|
||||
return;
|
||||
}
|
||||
for (String s : sInstance.mLog) {
|
||||
pw.println(s);
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
|
||||
public static synchronized void wtf(String tag, String s, Throwable here) {
|
||||
if (!ENABLED) {
|
||||
return;
|
||||
}
|
||||
e(tag, s);
|
||||
|
||||
Application application = ActivityThread.currentApplication();
|
||||
String fileMessage = "";
|
||||
if (Build.IS_DEBUGGABLE && application != null) {
|
||||
File f = new File(application.getDataDir(), "falsing-"
|
||||
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt");
|
||||
PrintWriter pw = null;
|
||||
try {
|
||||
pw = new PrintWriter(f);
|
||||
dump(pw);
|
||||
pw.close();
|
||||
fileMessage = "Log written to " + f.getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to write falsing log", e);
|
||||
} finally {
|
||||
if (pw != null) {
|
||||
pw.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Unable to write log, build must be debuggable.");
|
||||
}
|
||||
|
||||
Log.wtf(TAG, tag + " " + s + "; " + fileMessage, here);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import android.view.MotionEvent;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
@@ -261,8 +262,7 @@ public class FalsingManagerFake implements FalsingManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw) {
|
||||
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,589 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.classifier;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.biometrics.BiometricSourceType;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.analytics.DataCollector;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* When the phone is locked, listens to touch, sensor and phone events and sends them to
|
||||
* DataCollector and HumanInteractionClassifier.
|
||||
*
|
||||
* It does not collect touch events when the bouncer shows up.
|
||||
*/
|
||||
public class FalsingManagerImpl implements FalsingManager {
|
||||
private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
|
||||
|
||||
private static final int[] CLASSIFIER_SENSORS = new int[] {
|
||||
Sensor.TYPE_PROXIMITY,
|
||||
};
|
||||
|
||||
private static final int[] COLLECTOR_SENSORS = new int[] {
|
||||
Sensor.TYPE_ACCELEROMETER,
|
||||
Sensor.TYPE_GYROSCOPE,
|
||||
Sensor.TYPE_PROXIMITY,
|
||||
Sensor.TYPE_LIGHT,
|
||||
Sensor.TYPE_ROTATION_VECTOR,
|
||||
};
|
||||
public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts";
|
||||
public static final String FALSING_SUCCESS = "falsing_success_after_attempts";
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final Context mContext;
|
||||
|
||||
private final SensorManager mSensorManager;
|
||||
private final DataCollector mDataCollector;
|
||||
private final HumanInteractionClassifier mHumanInteractionClassifier;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final Executor mUiBgExecutor;
|
||||
|
||||
private boolean mEnforceBouncer = false;
|
||||
private boolean mBouncerOn = false;
|
||||
private boolean mBouncerOffOnDown = false;
|
||||
private boolean mSessionActive = false;
|
||||
private boolean mIsTouchScreen = true;
|
||||
private boolean mJustUnlockedWithFace = false;
|
||||
private int mState = StatusBarState.SHADE;
|
||||
private boolean mScreenOn;
|
||||
private boolean mShowingAod;
|
||||
private Runnable mPendingWtf;
|
||||
private int mIsFalseTouchCalls;
|
||||
private MetricsLogger mMetricsLogger;
|
||||
|
||||
private SensorEventListener mSensorEventListener = new SensorEventListener() {
|
||||
@Override
|
||||
public synchronized void onSensorChanged(SensorEvent event) {
|
||||
mDataCollector.onSensorChanged(event);
|
||||
mHumanInteractionClassifier.onSensorChanged(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
mDataCollector.onAccuracyChanged(sensor, accuracy);
|
||||
}
|
||||
};
|
||||
|
||||
public StateListener mStatusBarStateListener = new StateListener() {
|
||||
@Override
|
||||
public void onStateChanged(int newState) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("setStatusBarState", new StringBuilder()
|
||||
.append("from=").append(StatusBarState.toShortString(mState))
|
||||
.append(" to=").append(StatusBarState.toShortString(newState))
|
||||
.toString());
|
||||
}
|
||||
mState = newState;
|
||||
updateSessionActive();
|
||||
}
|
||||
};
|
||||
|
||||
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateConfiguration();
|
||||
}
|
||||
};
|
||||
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
|
||||
new KeyguardUpdateMonitorCallback() {
|
||||
@Override
|
||||
public void onBiometricAuthenticated(int userId,
|
||||
BiometricSourceType biometricSourceType,
|
||||
boolean isStrongBiometric) {
|
||||
if (userId == KeyguardUpdateMonitor.getCurrentUser()
|
||||
&& biometricSourceType == BiometricSourceType.FACE) {
|
||||
mJustUnlockedWithFace = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FalsingManagerImpl(Context context, @UiBackground Executor uiBgExecutor) {
|
||||
mContext = context;
|
||||
mSensorManager = Dependency.get(AsyncSensorManager.class);
|
||||
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
||||
mDataCollector = DataCollector.getInstance(mContext);
|
||||
mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
|
||||
mUiBgExecutor = uiBgExecutor;
|
||||
mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
|
||||
mMetricsLogger = new MetricsLogger();
|
||||
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
|
||||
mSettingsObserver,
|
||||
UserHandle.USER_ALL);
|
||||
|
||||
updateConfiguration();
|
||||
Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
|
||||
Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mKeyguardUpdateCallback);
|
||||
}
|
||||
|
||||
private void updateConfiguration() {
|
||||
mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
ENFORCE_BOUNCER, 0);
|
||||
}
|
||||
|
||||
private boolean shouldSessionBeActive() {
|
||||
if (FalsingLog.ENABLED && FalsingLog.VERBOSE) {
|
||||
FalsingLog.v("shouldBeActive", new StringBuilder()
|
||||
.append("enabled=").append(isEnabled() ? 1 : 0)
|
||||
.append(" mScreenOn=").append(mScreenOn ? 1 : 0)
|
||||
.append(" mState=").append(StatusBarState.toShortString(mState))
|
||||
.append(" mShowingAod=").append(mShowingAod ? 1 : 0)
|
||||
.toString()
|
||||
);
|
||||
}
|
||||
return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
|
||||
}
|
||||
|
||||
private boolean sessionEntrypoint() {
|
||||
if (!mSessionActive && shouldSessionBeActive()) {
|
||||
onSessionStart();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void sessionExitpoint(boolean force) {
|
||||
if (mSessionActive && (force || !shouldSessionBeActive())) {
|
||||
mSessionActive = false;
|
||||
if (mIsFalseTouchCalls != 0) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i(
|
||||
"isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls);
|
||||
}
|
||||
mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls);
|
||||
mIsFalseTouchCalls = 0;
|
||||
}
|
||||
|
||||
// This can be expensive, and doesn't need to happen on the main thread.
|
||||
mUiBgExecutor.execute(() -> {
|
||||
mSensorManager.unregisterListener(mSensorEventListener);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSessionActive() {
|
||||
if (shouldSessionBeActive()) {
|
||||
sessionEntrypoint();
|
||||
} else {
|
||||
sessionExitpoint(false /* force */);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSessionStart() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassifierEnabled());
|
||||
clearPendingWtf();
|
||||
}
|
||||
mBouncerOn = false;
|
||||
mSessionActive = true;
|
||||
mJustUnlockedWithFace = false;
|
||||
mIsFalseTouchCalls = 0;
|
||||
|
||||
if (mHumanInteractionClassifier.isEnabled()) {
|
||||
registerSensors(CLASSIFIER_SENSORS);
|
||||
}
|
||||
if (mDataCollector.isEnabledFull()) {
|
||||
registerSensors(COLLECTOR_SENSORS);
|
||||
}
|
||||
if (mDataCollector.isEnabled()) {
|
||||
mDataCollector.onFalsingSessionStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerSensors(int [] sensors) {
|
||||
for (int sensorType : sensors) {
|
||||
Sensor s = mSensorManager.getDefaultSensor(sensorType);
|
||||
if (s != null) {
|
||||
|
||||
// This can be expensive, and doesn't need to happen on the main thread.
|
||||
mUiBgExecutor.execute(() -> {
|
||||
mSensorManager.registerListener(
|
||||
mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isClassifierEnabled() {
|
||||
return mHumanInteractionClassifier.isEnabled();
|
||||
}
|
||||
|
||||
private boolean isEnabled() {
|
||||
return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
|
||||
}
|
||||
|
||||
public boolean isUnlockingDisabled() {
|
||||
return mDataCollector.isUnlockingDisabled();
|
||||
}
|
||||
/**
|
||||
* @return true if the classifier determined that this is not a human interacting with the phone
|
||||
*/
|
||||
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
// We're getting some false wtfs from touches that happen after the device went
|
||||
// to sleep. Only report missing sessions that happen when the device is interactive.
|
||||
if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()
|
||||
&& mPendingWtf == null) {
|
||||
int enabled = isEnabled() ? 1 : 0;
|
||||
int screenOn = mScreenOn ? 1 : 0;
|
||||
String state = StatusBarState.toShortString(mState);
|
||||
Throwable here = new Throwable("here");
|
||||
FalsingLog.wLogcat("isFalseTouch", new StringBuilder()
|
||||
.append("Session is not active, yet there's a query for a false touch.")
|
||||
.append(" enabled=").append(enabled)
|
||||
.append(" mScreenOn=").append(screenOn)
|
||||
.append(" mState=").append(state)
|
||||
.append(". Escalating to WTF if screen does not turn on soon.")
|
||||
.toString());
|
||||
|
||||
// Unfortunately we're also getting false positives for touches that happen right
|
||||
// after the screen turns on, but before that notification has made it to us.
|
||||
// Unfortunately there's no good way to catch that, except to wait and see if we get
|
||||
// the screen on notification soon.
|
||||
mPendingWtf = () -> FalsingLog.wtf("isFalseTouch", new StringBuilder()
|
||||
.append("Session did not become active after query for a false touch.")
|
||||
.append(" enabled=").append(enabled)
|
||||
.append('/').append(isEnabled() ? 1 : 0)
|
||||
.append(" mScreenOn=").append(screenOn)
|
||||
.append('/').append(mScreenOn ? 1 : 0)
|
||||
.append(" mState=").append(state)
|
||||
.append('/').append(StatusBarState.toShortString(mState))
|
||||
.append(". Look for warnings ~1000ms earlier to see root cause.")
|
||||
.toString(), here);
|
||||
mHandler.postDelayed(mPendingWtf, 1000);
|
||||
}
|
||||
}
|
||||
if (ActivityManager.isRunningInUserTestHarness()) {
|
||||
// This is a test device running UiAutomator code.
|
||||
return false;
|
||||
}
|
||||
if (mAccessibilityManager.isTouchExplorationEnabled()) {
|
||||
// Touch exploration triggers false positives in the classifier and
|
||||
// already sufficiently prevents false unlocks.
|
||||
return false;
|
||||
}
|
||||
if (!mIsTouchScreen) {
|
||||
// Unlocking with input devices besides the touchscreen should already be sufficiently
|
||||
// anti-falsed.
|
||||
return false;
|
||||
}
|
||||
if (mJustUnlockedWithFace) {
|
||||
// Unlocking with face is a strong user presence signal, we can assume the user
|
||||
// is present until the next session starts.
|
||||
return false;
|
||||
}
|
||||
mIsFalseTouchCalls++;
|
||||
boolean isFalse = mHumanInteractionClassifier.isFalseTouch();
|
||||
if (!isFalse) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls);
|
||||
}
|
||||
mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
|
||||
mIsFalseTouchCalls = 0;
|
||||
}
|
||||
return isFalse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFalseTap(boolean robustCheck) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFalseDoubleTap() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void clearPendingWtf() {
|
||||
if (mPendingWtf != null) {
|
||||
mHandler.removeCallbacks(mPendingWtf);
|
||||
mPendingWtf = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean shouldEnforceBouncer() {
|
||||
return mEnforceBouncer;
|
||||
}
|
||||
|
||||
public void setShowingAod(boolean showingAod) {
|
||||
mShowingAod = showingAod;
|
||||
updateSessionActive();
|
||||
}
|
||||
|
||||
public void onScreenTurningOn() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onScreenTurningOn", new StringBuilder()
|
||||
.append("from=").append(mScreenOn ? 1 : 0)
|
||||
.toString());
|
||||
clearPendingWtf();
|
||||
}
|
||||
mScreenOn = true;
|
||||
if (sessionEntrypoint()) {
|
||||
mDataCollector.onScreenTurningOn();
|
||||
}
|
||||
}
|
||||
|
||||
public void onScreenOnFromTouch() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
|
||||
.append("from=").append(mScreenOn ? 1 : 0)
|
||||
.toString());
|
||||
}
|
||||
mScreenOn = true;
|
||||
if (sessionEntrypoint()) {
|
||||
mDataCollector.onScreenOnFromTouch();
|
||||
}
|
||||
}
|
||||
|
||||
public void onScreenOff() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onScreenOff", new StringBuilder()
|
||||
.append("from=").append(mScreenOn ? 1 : 0)
|
||||
.toString());
|
||||
}
|
||||
mDataCollector.onScreenOff();
|
||||
mScreenOn = false;
|
||||
sessionExitpoint(false /* force */);
|
||||
}
|
||||
|
||||
public void onSuccessfulUnlock() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onSucccessfulUnlock", "");
|
||||
}
|
||||
mDataCollector.onSucccessfulUnlock();
|
||||
}
|
||||
|
||||
public void onBouncerShown() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onBouncerShown", new StringBuilder()
|
||||
.append("from=").append(mBouncerOn ? 1 : 0)
|
||||
.toString());
|
||||
}
|
||||
if (!mBouncerOn) {
|
||||
mBouncerOn = true;
|
||||
mDataCollector.onBouncerShown();
|
||||
}
|
||||
}
|
||||
|
||||
public void onBouncerHidden() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onBouncerHidden", new StringBuilder()
|
||||
.append("from=").append(mBouncerOn ? 1 : 0)
|
||||
.toString());
|
||||
}
|
||||
if (mBouncerOn) {
|
||||
mBouncerOn = false;
|
||||
mDataCollector.onBouncerHidden();
|
||||
}
|
||||
}
|
||||
|
||||
public void onQsDown() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onQsDown", "");
|
||||
}
|
||||
mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
|
||||
mDataCollector.onQsDown();
|
||||
}
|
||||
|
||||
public void setQsExpanded(boolean expanded) {
|
||||
mDataCollector.setQsExpanded(expanded);
|
||||
}
|
||||
|
||||
public void onTrackingStarted(boolean secure) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onTrackingStarted", "");
|
||||
}
|
||||
mHumanInteractionClassifier.setType(secure
|
||||
? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK);
|
||||
mDataCollector.onTrackingStarted();
|
||||
}
|
||||
|
||||
public void onTrackingStopped() {
|
||||
mDataCollector.onTrackingStopped();
|
||||
}
|
||||
|
||||
public void onNotificationActive() {
|
||||
mDataCollector.onNotificationActive();
|
||||
}
|
||||
|
||||
public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onNotificationDoubleTap", "accepted=" + accepted
|
||||
+ " dx=" + dx + " dy=" + dy + " (px)");
|
||||
}
|
||||
mDataCollector.onNotificationDoubleTap();
|
||||
}
|
||||
|
||||
public void setNotificationExpanded() {
|
||||
mDataCollector.setNotificationExpanded();
|
||||
}
|
||||
|
||||
public void onNotificatonStartDraggingDown() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onNotificatonStartDraggingDown", "");
|
||||
}
|
||||
mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
|
||||
mDataCollector.onNotificatonStartDraggingDown();
|
||||
}
|
||||
|
||||
public void onStartExpandingFromPulse() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onStartExpandingFromPulse", "");
|
||||
}
|
||||
mHumanInteractionClassifier.setType(Classifier.PULSE_EXPAND);
|
||||
mDataCollector.onStartExpandingFromPulse();
|
||||
}
|
||||
|
||||
public void onNotificatonStopDraggingDown() {
|
||||
mDataCollector.onNotificatonStopDraggingDown();
|
||||
}
|
||||
|
||||
public void onExpansionFromPulseStopped() {
|
||||
mDataCollector.onExpansionFromPulseStopped();
|
||||
}
|
||||
|
||||
public void onNotificationDismissed() {
|
||||
mDataCollector.onNotificationDismissed();
|
||||
}
|
||||
|
||||
public void onNotificationStartDismissing() {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onNotificationStartDismissing", "");
|
||||
}
|
||||
mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
|
||||
mDataCollector.onNotificatonStartDismissing();
|
||||
}
|
||||
|
||||
public void onNotificationStopDismissing() {
|
||||
mDataCollector.onNotificatonStopDismissing();
|
||||
}
|
||||
|
||||
public void onCameraOn() {
|
||||
mDataCollector.onCameraOn();
|
||||
}
|
||||
|
||||
public void onLeftAffordanceOn() {
|
||||
mDataCollector.onLeftAffordanceOn();
|
||||
}
|
||||
|
||||
public void onAffordanceSwipingStarted(boolean rightCorner) {
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("onAffordanceSwipingStarted", "");
|
||||
}
|
||||
if (rightCorner) {
|
||||
mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
|
||||
} else {
|
||||
mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
|
||||
}
|
||||
mDataCollector.onAffordanceSwipingStarted(rightCorner);
|
||||
}
|
||||
|
||||
public void onAffordanceSwipingAborted() {
|
||||
mDataCollector.onAffordanceSwipingAborted();
|
||||
}
|
||||
|
||||
public void onUnlockHintStarted() {
|
||||
mDataCollector.onUnlockHintStarted();
|
||||
}
|
||||
|
||||
public void onCameraHintStarted() {
|
||||
mDataCollector.onCameraHintStarted();
|
||||
}
|
||||
|
||||
public void onLeftAffordanceHintStarted() {
|
||||
mDataCollector.onLeftAffordanceHintStarted();
|
||||
}
|
||||
|
||||
public void onTouchEvent(MotionEvent event, int width, int height) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
// If the bouncer was not shown during the down event,
|
||||
// we want the entire gesture going to HumanInteractionClassifier
|
||||
mBouncerOffOnDown = !mBouncerOn;
|
||||
}
|
||||
if (mSessionActive) {
|
||||
if (!mBouncerOn) {
|
||||
// In case bouncer is "visible", but onFullyShown has not yet been called,
|
||||
// avoid adding the event to DataCollector
|
||||
mDataCollector.onTouchEvent(event, width, height);
|
||||
}
|
||||
if (mBouncerOffOnDown) {
|
||||
mHumanInteractionClassifier.onTouchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println("FALSING MANAGER");
|
||||
pw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0);
|
||||
pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
|
||||
pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
|
||||
pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
|
||||
pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
|
||||
pw.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
mSensorManager.unregisterListener(mSensorEventListener);
|
||||
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
|
||||
Dependency.get(StatusBarStateController.class).removeCallback(mStatusBarStateListener);
|
||||
Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mKeyguardUpdateCallback);
|
||||
}
|
||||
|
||||
public Uri reportRejectedTouch() {
|
||||
if (mDataCollector.isEnabled()) {
|
||||
return mDataCollector.reportRejectedTouch();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isReportingEnabled() {
|
||||
return mDataCollector.isReportingEnabled();
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.SensorManager;
|
||||
@@ -35,7 +33,6 @@ import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
|
||||
import com.android.systemui.classifier.brightline.FalsingDataProvider;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
@@ -55,41 +52,48 @@ import javax.inject.Inject;
|
||||
/**
|
||||
* Simple passthrough implementation of {@link FalsingManager} allowing plugins to swap in.
|
||||
*
|
||||
* {@link FalsingManagerImpl} is used when a Plugin is not loaded.
|
||||
* {@link BrightLineFalsingManager} is used when a Plugin is not loaded.
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class FalsingManagerProxy implements FalsingManager, Dumpable {
|
||||
|
||||
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
|
||||
private static final String DUMPABLE_TAG = "FalsingManager";
|
||||
public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts";
|
||||
public static final String FALSING_SUCCESS = "falsing_success_after_attempts";
|
||||
|
||||
private final PluginManager mPluginManager;
|
||||
private final ProximitySensor mProximitySensor;
|
||||
private final Resources mResources;
|
||||
private final ViewConfiguration mViewConfiguration;
|
||||
private final FalsingDataProvider mFalsingDataProvider;
|
||||
private FalsingManager mInternalFalsingManager;
|
||||
private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
|
||||
private final DeviceConfigProxy mDeviceConfig;
|
||||
private boolean mBrightlineEnabled;
|
||||
private final DockManager mDockManager;
|
||||
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
private Executor mUiBgExecutor;
|
||||
private final DumpManager mDumpManager;
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
final PluginListener<FalsingPlugin> mPluginListener;
|
||||
|
||||
private FalsingManager mInternalFalsingManager;
|
||||
|
||||
private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener =
|
||||
properties -> onDeviceConfigPropertiesChanged(properties.getNamespace());
|
||||
|
||||
@Inject
|
||||
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
|
||||
FalsingManagerProxy(PluginManager pluginManager, @Main Executor executor,
|
||||
ProximitySensor proximitySensor,
|
||||
DeviceConfigProxy deviceConfig, DockManager dockManager,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor,
|
||||
DumpManager dumpManager,
|
||||
@UiBackground Executor uiBgExecutor,
|
||||
StatusBarStateController statusBarStateController,
|
||||
@Main Resources resources,
|
||||
ViewConfiguration viewConfiguration,
|
||||
FalsingDataProvider falsingDataProvider) {
|
||||
mPluginManager = pluginManager;
|
||||
mProximitySensor = proximitySensor;
|
||||
mDockManager = dockManager;
|
||||
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
|
||||
mUiBgExecutor = uiBgExecutor;
|
||||
mDumpManager = dumpManager;
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mResources = resources;
|
||||
mViewConfiguration = viewConfiguration;
|
||||
@@ -97,16 +101,14 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
|
||||
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
|
||||
mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
|
||||
mDeviceConfig = deviceConfig;
|
||||
mDeviceConfigListener =
|
||||
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
|
||||
setupFalsingManager(context);
|
||||
setupFalsingManager();
|
||||
mDeviceConfig.addOnPropertiesChangedListener(
|
||||
DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
executor,
|
||||
mDeviceConfigListener
|
||||
);
|
||||
|
||||
final PluginListener<FalsingPlugin> mPluginListener = new PluginListener<FalsingPlugin>() {
|
||||
mPluginListener = new PluginListener<FalsingPlugin>() {
|
||||
public void onPluginConnected(FalsingPlugin plugin, Context context) {
|
||||
FalsingManager pluginFalsingManager = plugin.getFalsingManager(context);
|
||||
if (pluginFalsingManager != null) {
|
||||
@@ -116,51 +118,42 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
|
||||
}
|
||||
|
||||
public void onPluginDisconnected(FalsingPlugin plugin) {
|
||||
mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
|
||||
setupFalsingManager();
|
||||
}
|
||||
};
|
||||
|
||||
pluginManager.addPluginListener(mPluginListener, FalsingPlugin.class);
|
||||
mPluginManager.addPluginListener(mPluginListener, FalsingPlugin.class);
|
||||
|
||||
dumpManager.registerDumpable("FalsingManager", this);
|
||||
mDumpManager.registerDumpable(DUMPABLE_TAG, this);
|
||||
}
|
||||
|
||||
private void onDeviceConfigPropertiesChanged(Context context, String namespace) {
|
||||
private void onDeviceConfigPropertiesChanged(String namespace) {
|
||||
if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupFalsingManager(context);
|
||||
setupFalsingManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses the FalsingManager implementation.
|
||||
* Setup the FalsingManager implementation.
|
||||
*
|
||||
* If multiple implementations are available, this is where the choice is made.
|
||||
*/
|
||||
private void setupFalsingManager(Context context) {
|
||||
boolean brightlineEnabled = mDeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true);
|
||||
if (brightlineEnabled == mBrightlineEnabled && mInternalFalsingManager != null) {
|
||||
return;
|
||||
}
|
||||
mBrightlineEnabled = brightlineEnabled;
|
||||
|
||||
private void setupFalsingManager() {
|
||||
if (mInternalFalsingManager != null) {
|
||||
mInternalFalsingManager.cleanup();
|
||||
}
|
||||
if (!brightlineEnabled) {
|
||||
mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
|
||||
} else {
|
||||
mInternalFalsingManager = new BrightLineFalsingManager(
|
||||
mFalsingDataProvider,
|
||||
mKeyguardUpdateMonitor,
|
||||
mProximitySensor,
|
||||
mDeviceConfig,
|
||||
mResources,
|
||||
mViewConfiguration,
|
||||
mDockManager,
|
||||
mStatusBarStateController
|
||||
);
|
||||
}
|
||||
mInternalFalsingManager = new BrightLineFalsingManager(
|
||||
mFalsingDataProvider,
|
||||
mKeyguardUpdateMonitor,
|
||||
mProximitySensor,
|
||||
mDeviceConfig,
|
||||
mResources,
|
||||
mViewConfiguration,
|
||||
mDockManager,
|
||||
mStatusBarStateController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,17 +352,14 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
|
||||
mInternalFalsingManager.dump(pw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw) {
|
||||
mInternalFalsingManager.dump(pw);
|
||||
mInternalFalsingManager.dump(fd, pw, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener);
|
||||
mPluginManager.removePluginListener(mPluginListener);
|
||||
mDumpManager.unregisterDumpable(DUMPABLE_TAG);
|
||||
mInternalFalsingManager.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* An abstract class for classifiers which classify the whole gesture (all the strokes which
|
||||
* occurred from DOWN event to UP/CANCEL event)
|
||||
*/
|
||||
public abstract class GestureClassifier extends Classifier {
|
||||
|
||||
/**
|
||||
* @param type the type of action for which this method is called
|
||||
* @return a non-negative value which is used to determine whether the most recent gesture is a
|
||||
* false interaction; the bigger the value the greater the chance that this a false
|
||||
* interaction.
|
||||
*/
|
||||
public abstract float getFalseTouchEvaluation(int type);
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Holds the evaluations for ended strokes and gestures. These values are decreased through time.
|
||||
*/
|
||||
public class HistoryEvaluator {
|
||||
private static final float INTERVAL = 50.0f;
|
||||
private static final float HISTORY_FACTOR = 0.9f;
|
||||
private static final float EPSILON = 1e-5f;
|
||||
|
||||
private final ArrayList<Data> mStrokes = new ArrayList<>();
|
||||
private final ArrayList<Data> mGestureWeights = new ArrayList<>();
|
||||
private long mLastUpdate;
|
||||
|
||||
public HistoryEvaluator() {
|
||||
mLastUpdate = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
public void addStroke(float evaluation) {
|
||||
decayValue();
|
||||
mStrokes.add(new Data(evaluation));
|
||||
}
|
||||
|
||||
public void addGesture(float evaluation) {
|
||||
decayValue();
|
||||
mGestureWeights.add(new Data(evaluation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the weighted average of strokes and adds to it the weighted average of gestures
|
||||
*/
|
||||
public float getEvaluation() {
|
||||
return weightedAverage(mStrokes) + weightedAverage(mGestureWeights);
|
||||
}
|
||||
|
||||
private float weightedAverage(ArrayList<Data> list) {
|
||||
float sumValue = 0.0f;
|
||||
float sumWeight = 0.0f;
|
||||
int size = list.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Data data = list.get(i);
|
||||
sumValue += data.evaluation * data.weight;
|
||||
sumWeight += data.weight;
|
||||
}
|
||||
|
||||
if (sumWeight == 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return sumValue / sumWeight;
|
||||
}
|
||||
|
||||
private void decayValue() {
|
||||
long time = SystemClock.elapsedRealtime();
|
||||
|
||||
if (time <= mLastUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds.
|
||||
float factor = (float) Math.pow(HISTORY_FACTOR, (time - mLastUpdate) / INTERVAL);
|
||||
|
||||
decayValue(mStrokes, factor);
|
||||
decayValue(mGestureWeights, factor);
|
||||
mLastUpdate = time;
|
||||
}
|
||||
|
||||
private void decayValue(ArrayList<Data> list, float factor) {
|
||||
int size = list.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
list.get(i).weight *= factor;
|
||||
}
|
||||
|
||||
// Removing evaluations with such small weights that they do not matter anymore
|
||||
while (!list.isEmpty() && isZero(list.get(0).weight)) {
|
||||
list.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isZero(float x) {
|
||||
return x <= EPSILON && x >= -EPSILON;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each stroke it holds its initial value and the current weight. Initially the
|
||||
* weight is set to 1.0
|
||||
*/
|
||||
private static class Data {
|
||||
public float evaluation;
|
||||
public float weight;
|
||||
|
||||
public Data(float evaluation) {
|
||||
this.evaluation = evaluation;
|
||||
weight = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
/**
|
||||
* An classifier trying to determine whether it is a human interacting with the phone or not.
|
||||
*/
|
||||
public class HumanInteractionClassifier extends Classifier {
|
||||
private static final String HIC_ENABLE = "HIC_enable";
|
||||
private static final float FINGER_DISTANCE = 0.1f;
|
||||
|
||||
private static HumanInteractionClassifier sInstance = null;
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final Context mContext;
|
||||
|
||||
private final StrokeClassifier[] mStrokeClassifiers;
|
||||
private final GestureClassifier[] mGestureClassifiers;
|
||||
private final ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
|
||||
private final HistoryEvaluator mHistoryEvaluator;
|
||||
private final float mDpi;
|
||||
|
||||
private boolean mEnableClassifier = false;
|
||||
private int mCurrentType = Classifier.GENERIC;
|
||||
|
||||
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateConfiguration();
|
||||
}
|
||||
};
|
||||
|
||||
private HumanInteractionClassifier(Context context) {
|
||||
mContext = context;
|
||||
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
|
||||
|
||||
// If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
|
||||
// were to be used separately. Due negligible differences in xdpi and ydpi we can just
|
||||
// take the average.
|
||||
// Note that xdpi and ydpi are the physical pixels per inch and are not affected by scaling.
|
||||
mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
|
||||
mClassifierData = new ClassifierData(mDpi);
|
||||
mHistoryEvaluator = new HistoryEvaluator();
|
||||
|
||||
mStrokeClassifiers = new StrokeClassifier[]{
|
||||
new AnglesClassifier(mClassifierData),
|
||||
new SpeedClassifier(mClassifierData),
|
||||
new DurationCountClassifier(mClassifierData),
|
||||
new EndPointRatioClassifier(mClassifierData),
|
||||
new EndPointLengthClassifier(mClassifierData),
|
||||
new AccelerationClassifier(mClassifierData),
|
||||
new SpeedAnglesClassifier(mClassifierData),
|
||||
new LengthCountClassifier(mClassifierData),
|
||||
new DirectionClassifier(mClassifierData),
|
||||
};
|
||||
|
||||
mGestureClassifiers = new GestureClassifier[] {
|
||||
new PointerCountClassifier(mClassifierData),
|
||||
new ProximityClassifier(mClassifierData)
|
||||
};
|
||||
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(HIC_ENABLE), false,
|
||||
mSettingsObserver,
|
||||
UserHandle.USER_ALL);
|
||||
|
||||
updateConfiguration();
|
||||
}
|
||||
|
||||
public static HumanInteractionClassifier getInstance(Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new HumanInteractionClassifier(context);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private void updateConfiguration() {
|
||||
boolean defaultValue = mContext.getResources().getBoolean(
|
||||
R.bool.config_lockscreenAntiFalsingClassifierEnabled);
|
||||
|
||||
mEnableClassifier = 0 != Settings.Global.getInt(
|
||||
mContext.getContentResolver(),
|
||||
HIC_ENABLE, defaultValue ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
mCurrentType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
if (!mEnableClassifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user is dragging down the notification, they might want to drag it down
|
||||
// enough to see the content, read it for a while and then lift the finger to open
|
||||
// the notification. This kind of motion scores very bad in the Classifier so the
|
||||
// MotionEvents which are close to the current position of the finger are not
|
||||
// sent to the classifiers until the finger moves far enough. When the finger if lifted
|
||||
// up, the last MotionEvent which was far enough from the finger is set as the final
|
||||
// MotionEvent and sent to the Classifiers.
|
||||
if (mCurrentType == Classifier.NOTIFICATION_DRAG_DOWN
|
||||
|| mCurrentType == Classifier.PULSE_EXPAND) {
|
||||
mBufferedEvents.add(MotionEvent.obtain(event));
|
||||
Point pointEnd = new Point(event.getX() / mDpi, event.getY() / mDpi);
|
||||
|
||||
while (pointEnd.dist(new Point(mBufferedEvents.getFirst().getX() / mDpi,
|
||||
mBufferedEvents.getFirst().getY() / mDpi)) > FINGER_DISTANCE) {
|
||||
addTouchEvent(mBufferedEvents.getFirst());
|
||||
mBufferedEvents.remove();
|
||||
}
|
||||
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
mBufferedEvents.getFirst().setAction(MotionEvent.ACTION_UP);
|
||||
addTouchEvent(mBufferedEvents.getFirst());
|
||||
mBufferedEvents.clear();
|
||||
}
|
||||
} else {
|
||||
addTouchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTouchEvent(MotionEvent event) {
|
||||
if (!mClassifierData.update(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (StrokeClassifier c : mStrokeClassifiers) {
|
||||
c.onTouchEvent(event);
|
||||
}
|
||||
|
||||
for (GestureClassifier c : mGestureClassifiers) {
|
||||
c.onTouchEvent(event);
|
||||
}
|
||||
|
||||
int size = mClassifierData.getEndingStrokes().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Stroke stroke = mClassifierData.getEndingStrokes().get(i);
|
||||
float evaluation = 0.0f;
|
||||
StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("stroke") : null;
|
||||
for (StrokeClassifier c : mStrokeClassifiers) {
|
||||
float e = c.getFalseTouchEvaluation(mCurrentType, stroke);
|
||||
if (FalsingLog.ENABLED) {
|
||||
String tag = c.getTag();
|
||||
sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
|
||||
}
|
||||
evaluation += e;
|
||||
}
|
||||
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i(" addTouchEvent", sb.toString());
|
||||
}
|
||||
mHistoryEvaluator.addStroke(evaluation);
|
||||
}
|
||||
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
float evaluation = 0.0f;
|
||||
StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("gesture") : null;
|
||||
for (GestureClassifier c : mGestureClassifiers) {
|
||||
float e = c.getFalseTouchEvaluation(mCurrentType);
|
||||
if (FalsingLog.ENABLED) {
|
||||
String tag = c.getTag();
|
||||
sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
|
||||
}
|
||||
evaluation += e;
|
||||
}
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i(" addTouchEvent", sb.toString());
|
||||
}
|
||||
mHistoryEvaluator.addGesture(evaluation);
|
||||
setType(Classifier.GENERIC);
|
||||
}
|
||||
|
||||
mClassifierData.cleanUp(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
for (Classifier c : mStrokeClassifiers) {
|
||||
c.onSensorChanged(event);
|
||||
}
|
||||
|
||||
for (Classifier c : mGestureClassifiers) {
|
||||
c.onSensorChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFalseTouch() {
|
||||
if (mEnableClassifier) {
|
||||
float evaluation = mHistoryEvaluator.getEvaluation();
|
||||
boolean result = evaluation >= 5.0f;
|
||||
if (FalsingLog.ENABLED) {
|
||||
FalsingLog.i("isFalseTouch", new StringBuilder()
|
||||
.append("eval=").append(evaluation).append(" result=")
|
||||
.append(result ? 1 : 0).toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return mEnableClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "HIC";
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the ratio between the length of the stroke and its number of
|
||||
* points. The number of points is subtracted by 2 because the UP event comes in with some delay
|
||||
* and it should not influence the ratio and also strokes which are long and have a small number
|
||||
* of points are punished more (these kind of strokes are usually bad ones and they tend to score
|
||||
* well in other classifiers).
|
||||
*/
|
||||
public class LengthCountClassifier extends StrokeClassifier {
|
||||
public LengthCountClassifier(ClassifierData classifierData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "LEN_CNT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
return LengthCountEvaluator.evaluate(stroke.getTotalLength()
|
||||
/ Math.max(1.0f, stroke.getCount() - 2));
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the ratio between the length of the stroke and its number of
|
||||
* points.
|
||||
*/
|
||||
public class LengthCountEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value < 0.09) evaluation++;
|
||||
if (value < 0.05) evaluation++;
|
||||
if (value < 0.02) evaluation++;
|
||||
if (value > 0.6) evaluation++;
|
||||
if (value > 0.9) evaluation++;
|
||||
if (value > 1.2) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class Point {
|
||||
public float x;
|
||||
public float y;
|
||||
public long timeOffsetNano;
|
||||
|
||||
public Point(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.timeOffsetNano = 0;
|
||||
}
|
||||
|
||||
public Point(float x, float y, long timeOffsetNano) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.timeOffsetNano = timeOffsetNano;
|
||||
}
|
||||
|
||||
public boolean equals(Point p) {
|
||||
return x == p.x && y == p.y;
|
||||
}
|
||||
|
||||
public float dist(Point a) {
|
||||
return (float) Math.hypot(a.x - x, a.y - y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cross product of vec(this, a) and vec(this, b) where vec(x,y) is the
|
||||
* vector from point x to point y
|
||||
*/
|
||||
public float crossProduct(Point a, Point b) {
|
||||
return (a.x - x) * (b.y - y) - (a.y - y) * (b.x - x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dot product of vec(this, a) and vec(this, b) where vec(x,y) is the
|
||||
* vector from point x to point y
|
||||
*/
|
||||
public float dotProduct(Point a, Point b) {
|
||||
return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the angle in radians created by points (a, this, b). If any two of these points
|
||||
* are the same, the method will return 0.0f
|
||||
*
|
||||
* @return the angle in radians
|
||||
*/
|
||||
public float getAngle(Point a, Point b) {
|
||||
float dist1 = dist(a);
|
||||
float dist2 = dist(b);
|
||||
|
||||
if (dist1 == 0.0f || dist2 == 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float crossProduct = crossProduct(a, b);
|
||||
float dotProduct = dotProduct(a, b);
|
||||
float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2));
|
||||
float angle = (float) Math.acos(cos);
|
||||
if (crossProduct < 0.0) {
|
||||
angle = 2.0f * (float) Math.PI - angle;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the total number of traces in the whole gesture.
|
||||
*/
|
||||
public class PointerCountClassifier extends GestureClassifier {
|
||||
private int mCount;
|
||||
|
||||
public PointerCountClassifier(ClassifierData classifierData) {
|
||||
mCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "PTR_CNT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
int action = event.getActionMasked();
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mCount = 1;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
++mCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type) {
|
||||
return PointerCountEvaluator.evaluate(mCount);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class PointerCountEvaluator {
|
||||
public static float evaluate(int value) {
|
||||
return (value - 1) * (value - 1);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* A classifier which looks at the proximity sensor during the gesture. It calculates the percentage
|
||||
* the proximity sensor showing the near state during the whole gesture
|
||||
*/
|
||||
public class ProximityClassifier extends GestureClassifier {
|
||||
private long mGestureStartTimeNano;
|
||||
private long mNearStartTimeNano;
|
||||
private long mNearDuration;
|
||||
private boolean mNear;
|
||||
private float mAverageNear;
|
||||
|
||||
public ProximityClassifier(ClassifierData classifierData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "PROX";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
|
||||
update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
int action = event.getActionMasked();
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mGestureStartTimeNano = event.getEventTimeNano();
|
||||
mNearStartTimeNano = event.getEventTimeNano();
|
||||
mNearDuration = 0;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
update(mNear, event.getEventTimeNano());
|
||||
long duration = event.getEventTimeNano() - mGestureStartTimeNano;
|
||||
|
||||
if (duration == 0) {
|
||||
mAverageNear = mNear ? 1.0f : 0.0f;
|
||||
} else {
|
||||
mAverageNear = (float) mNearDuration / (float) duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param near is the sensor showing the near state right now
|
||||
* @param timestampNano time of this event in nanoseconds
|
||||
*/
|
||||
private void update(boolean near, long timestampNano) {
|
||||
// This if is necessary because MotionEvents and SensorEvents do not come in
|
||||
// chronological order
|
||||
if (timestampNano > mNearStartTimeNano) {
|
||||
// if the state before was near then add the difference of the current time and
|
||||
// mNearStartTimeNano to mNearDuration.
|
||||
if (mNear) {
|
||||
mNearDuration += timestampNano - mNearStartTimeNano;
|
||||
}
|
||||
|
||||
// if the new state is near, set mNearStartTimeNano equal to this moment.
|
||||
if (near) {
|
||||
mNearStartTimeNano = timestampNano;
|
||||
}
|
||||
}
|
||||
mNear = near;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type) {
|
||||
return ProximityEvaluator.evaluate(mAverageNear, type);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class ProximityEvaluator {
|
||||
public static float evaluate(float value, int type) {
|
||||
float evaluation = 0.0f;
|
||||
float threshold = 0.1f;
|
||||
if (type == Classifier.QUICK_SETTINGS) {
|
||||
threshold = 1.0f;
|
||||
}
|
||||
if (value >= threshold) evaluation += 2.0;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.SystemProperties;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A classifier which for each point from a stroke, it creates a point on plane with coordinates
|
||||
* (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE)
|
||||
* and then it calculates the angle variance of these points like the class
|
||||
* {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores
|
||||
* the last point of a stroke because the UP event comes in with some delay and this ruins the
|
||||
* smoothness of this curve. Additionally, the classifier classifies calculates the percentage of
|
||||
* angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier
|
||||
* does that is because the speed of a good stroke is most often increases, so most of these angels
|
||||
* should be in this interval.
|
||||
*/
|
||||
public class SpeedAnglesClassifier extends StrokeClassifier {
|
||||
public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.spd_ang",
|
||||
Build.IS_DEBUGGABLE);
|
||||
public static final String TAG = "SPD_ANG";
|
||||
|
||||
private HashMap<Stroke, Data> mStrokeMap = new HashMap<>();
|
||||
|
||||
public SpeedAnglesClassifier(ClassifierData classifierData) {
|
||||
mClassifierData = classifierData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
int action = event.getActionMasked();
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mStrokeMap.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
Stroke stroke = mClassifierData.getStroke(event.getPointerId(i));
|
||||
|
||||
if (mStrokeMap.get(stroke) == null) {
|
||||
mStrokeMap.put(stroke, new Data());
|
||||
}
|
||||
|
||||
if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL
|
||||
&& !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) {
|
||||
mStrokeMap.get(stroke).addPoint(
|
||||
stroke.getPoints().get(stroke.getPoints().size() - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
Data data = mStrokeMap.get(stroke);
|
||||
return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance())
|
||||
+ SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage());
|
||||
}
|
||||
|
||||
private static class Data {
|
||||
private final float DURATION_SCALE = 1e8f;
|
||||
private final float LENGTH_SCALE = 1.0f;
|
||||
private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f;
|
||||
|
||||
private List<Point> mLastThreePoints = new ArrayList<>();
|
||||
private Point mPreviousPoint;
|
||||
private float mPreviousAngle;
|
||||
private float mSumSquares;
|
||||
private float mSum;
|
||||
private float mCount;
|
||||
private float mDist;
|
||||
private float mAnglesCount;
|
||||
private float mAcceleratingAngles;
|
||||
|
||||
public Data() {
|
||||
mPreviousPoint = null;
|
||||
mPreviousAngle = (float) Math.PI;
|
||||
mSumSquares = 0.0f;
|
||||
mSum = 0.0f;
|
||||
mCount = 1.0f;
|
||||
mDist = 0.0f;
|
||||
mAnglesCount = mAcceleratingAngles = 0.0f;
|
||||
}
|
||||
|
||||
public void addPoint(Point point) {
|
||||
if (mPreviousPoint != null) {
|
||||
mDist += mPreviousPoint.dist(point);
|
||||
}
|
||||
|
||||
mPreviousPoint = point;
|
||||
Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE,
|
||||
mDist / LENGTH_SCALE);
|
||||
|
||||
// Checking if the added point is different than the previously added point
|
||||
// Repetitions are being ignored so that proper angles are calculated.
|
||||
if (mLastThreePoints.isEmpty()
|
||||
|| !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) {
|
||||
mLastThreePoints.add(speedPoint);
|
||||
if (mLastThreePoints.size() == 4) {
|
||||
mLastThreePoints.remove(0);
|
||||
|
||||
float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0),
|
||||
mLastThreePoints.get(2));
|
||||
|
||||
mAnglesCount++;
|
||||
if (angle >= (float) Math.PI - ANGLE_DEVIATION) {
|
||||
mAcceleratingAngles++;
|
||||
}
|
||||
|
||||
float difference = angle - mPreviousAngle;
|
||||
mSum += difference;
|
||||
mSumSquares += difference * difference;
|
||||
mCount += 1.0;
|
||||
mPreviousAngle = angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float getAnglesVariance() {
|
||||
final float v = mSumSquares / mCount - (mSum / mCount) * (mSum / mCount);
|
||||
if (VERBOSE) {
|
||||
FalsingLog.i(TAG, "getAnglesVariance: sum^2=" + mSumSquares
|
||||
+ " count=" + mCount + " result=" + v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public float getAnglesPercentage() {
|
||||
if (mAnglesCount == 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
final float v = (mAcceleratingAngles) / mAnglesCount;
|
||||
if (VERBOSE) {
|
||||
FalsingLog.i(TAG, "getAnglesPercentage: angles=" + mAcceleratingAngles
|
||||
+ " count=" + mAnglesCount + " result=" + v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class SpeedAnglesPercentageEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value < 1.00) evaluation++;
|
||||
if (value < 0.90) evaluation++;
|
||||
if (value < 0.70) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in
|
||||
* inches per second.
|
||||
*/
|
||||
public class SpeedClassifier extends StrokeClassifier {
|
||||
private final float NANOS_TO_SECONDS = 1e9f;
|
||||
|
||||
public SpeedClassifier(ClassifierData classifierData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return "SPD";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFalseTouchEvaluation(int type, Stroke stroke) {
|
||||
float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
|
||||
if (duration == 0.0f) {
|
||||
return SpeedEvaluator.evaluate(0.0f);
|
||||
}
|
||||
return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class SpeedEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value < 4.0) evaluation++;
|
||||
if (value < 2.2) evaluation++;
|
||||
if (value > 35.0) evaluation++;
|
||||
if (value > 50.0) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class SpeedRatioEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value == 0) return 0;
|
||||
if (value <= 1.0) evaluation++;
|
||||
if (value <= 0.5) evaluation++;
|
||||
if (value > 9.0) evaluation++;
|
||||
if (value > 18.0) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
public class SpeedVarianceEvaluator {
|
||||
public static float evaluate(float value) {
|
||||
float evaluation = 0.0f;
|
||||
if (value > 0.06) evaluation++;
|
||||
if (value > 0.15) evaluation++;
|
||||
if (value > 0.3) evaluation++;
|
||||
if (value > 0.6) evaluation++;
|
||||
return evaluation;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Contains data about a stroke (a single trace, all the events from a given id from the
|
||||
* DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.)
|
||||
*/
|
||||
public class Stroke {
|
||||
private final float NANOS_TO_SECONDS = 1e9f;
|
||||
|
||||
private ArrayList<Point> mPoints = new ArrayList<>();
|
||||
private long mStartTimeNano;
|
||||
private long mEndTimeNano;
|
||||
private float mLength;
|
||||
private final float mDpi;
|
||||
|
||||
public Stroke(long eventTimeNano, float dpi) {
|
||||
mDpi = dpi;
|
||||
mStartTimeNano = mEndTimeNano = eventTimeNano;
|
||||
}
|
||||
|
||||
public void addPoint(float x, float y, long eventTimeNano) {
|
||||
mEndTimeNano = eventTimeNano;
|
||||
Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano);
|
||||
if (!mPoints.isEmpty()) {
|
||||
mLength += mPoints.get(mPoints.size() - 1).dist(point);
|
||||
}
|
||||
mPoints.add(point);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mPoints.size();
|
||||
}
|
||||
|
||||
public float getTotalLength() {
|
||||
return mLength;
|
||||
}
|
||||
|
||||
public float getEndPointLength() {
|
||||
return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1));
|
||||
}
|
||||
|
||||
public long getDurationNanos() {
|
||||
return mEndTimeNano - mStartTimeNano;
|
||||
}
|
||||
|
||||
public float getDurationSeconds() {
|
||||
return (float) getDurationNanos() / NANOS_TO_SECONDS;
|
||||
}
|
||||
|
||||
public ArrayList<Point> getPoints() {
|
||||
return mPoints;
|
||||
}
|
||||
|
||||
public long getLastEventTimeNano() {
|
||||
if (mPoints.isEmpty()) {
|
||||
return mStartTimeNano;
|
||||
}
|
||||
|
||||
return mPoints.get(mPoints.size() - 1).timeOffsetNano;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.classifier;
|
||||
|
||||
/**
|
||||
* An abstract class for classifiers which classify each stroke separately.
|
||||
*/
|
||||
public abstract class StrokeClassifier extends Classifier {
|
||||
|
||||
/**
|
||||
* @param type the type of action for which this method is called
|
||||
* @param stroke the stroke for which the evaluation will be calculated
|
||||
* @return a non-negative value which is used to determine whether this a false touch; the
|
||||
* bigger the value the greater the chance that this a false touch
|
||||
*/
|
||||
public abstract float getFalseTouchEvaluation(int type, Stroke stroke);
|
||||
}
|
||||
@@ -16,20 +16,22 @@
|
||||
|
||||
package com.android.systemui.classifier.brightline;
|
||||
|
||||
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED;
|
||||
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS;
|
||||
import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_REMAIN_LOCKED;
|
||||
import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.biometrics.BiometricSourceType;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
||||
import com.android.systemui.R;
|
||||
@@ -44,6 +46,7 @@ import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.sensors.ProximitySensor;
|
||||
import com.android.systemui.util.sensors.ThresholdSensor;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
@@ -457,7 +460,7 @@ public class BrightLineFalsingManager implements FalsingManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw) {
|
||||
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
|
||||
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
||||
ipw.println("BRIGHTLINE FALSING MANAGER");
|
||||
ipw.print("classifierEnabled=");
|
||||
|
||||
@@ -146,7 +146,6 @@ import com.android.systemui.SystemUI;
|
||||
import com.android.systemui.assist.AssistManager;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.charging.WirelessChargingAnimation;
|
||||
import com.android.systemui.classifier.FalsingLog;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.demomode.DemoMode;
|
||||
@@ -1238,10 +1237,6 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
message.write(SystemProperties.get("ro.serialno"));
|
||||
message.write("\n");
|
||||
|
||||
PrintWriter falsingPw = new PrintWriter(message);
|
||||
FalsingLog.dump(falsingPw);
|
||||
falsingPw.flush();
|
||||
|
||||
startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
|
||||
.setType("*/*")
|
||||
.putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
|
||||
@@ -2664,9 +2659,6 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
mLightBarController.dump(fd, pw, args);
|
||||
}
|
||||
|
||||
mFalsingManager.dump(pw);
|
||||
FalsingLog.dump(pw);
|
||||
|
||||
pw.println("SharedPreferences:");
|
||||
for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
|
||||
pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.classifier;
|
||||
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.logging.testing.UiEventLoggerFake;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
|
||||
import com.android.systemui.classifier.brightline.FalsingDataProvider;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.dock.DockManagerFake;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.DeviceConfigProxyFake;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.sensors.ProximitySensor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
import com.android.systemui.utils.leaks.FakeBatteryController;
|
||||
import com.android.systemui.utils.leaks.LeakCheckedTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class FalsingManagerProxyTest extends LeakCheckedTest {
|
||||
@Mock(stubOnly = true)
|
||||
PluginManager mPluginManager;
|
||||
@Mock(stubOnly = true)
|
||||
ProximitySensor mProximitySensor;
|
||||
@Mock(stubOnly = true)
|
||||
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
@Mock DumpManager mDumpManager;
|
||||
private FalsingManagerProxy mProxy;
|
||||
private DeviceConfigProxy mDeviceConfig;
|
||||
private FalsingDataProvider mFalsingDataProvider;
|
||||
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
private DockManager mDockManager = new DockManagerFake();
|
||||
private StatusBarStateController mStatusBarStateController =
|
||||
new StatusBarStateControllerImpl(new UiEventLoggerFake());
|
||||
@Mock private Resources mResources;
|
||||
@Mock private ViewConfiguration mViewConfiguration;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mDeviceConfig = new DeviceConfigProxyFake();
|
||||
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
|
||||
mFalsingDataProvider = new FalsingDataProvider(
|
||||
new DisplayMetrics(), new FakeBatteryController(getLeakCheck()),
|
||||
new FakeSystemClock());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (mProxy != null) {
|
||||
mProxy.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_brightLineFalsingManagerDisabled() {
|
||||
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
|
||||
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
|
||||
mDumpManager, mUiBgExecutor, mStatusBarStateController, mResources,
|
||||
mViewConfiguration, mFalsingDataProvider);
|
||||
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_brightLineFalsingManagerEnabled() throws InterruptedException {
|
||||
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
|
||||
mExecutor.runAllReady();
|
||||
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
|
||||
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
|
||||
mDumpManager, mUiBgExecutor, mStatusBarStateController, mResources,
|
||||
mViewConfiguration, mFalsingDataProvider);
|
||||
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
|
||||
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor,
|
||||
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
|
||||
mDumpManager, mUiBgExecutor, mStatusBarStateController, mResources,
|
||||
mViewConfiguration, mFalsingDataProvider);
|
||||
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
|
||||
|
||||
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
|
||||
mExecutor.runAllReady();
|
||||
assertThat(mProxy.getInternalFalsingManager(),
|
||||
instanceOf(BrightLineFalsingManager.class));
|
||||
|
||||
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
|
||||
mExecutor.runAllReady();
|
||||
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user