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:
Dave Mankoff
2020-11-12 15:28:31 -05:00
parent 040ffe7b34
commit 0181890251
40 changed files with 51 additions and 2903 deletions

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
*/

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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";
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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=");

View File

@@ -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());

View File

@@ -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));
}
}