1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.classifier;
18 
19 import android.view.MotionEvent;
20 
21 import com.android.systemui.plugins.FalsingManager;
22 
23 import java.util.List;
24 
25 /**
26  * Base class for rules that determine False touches.
27  */
28 public abstract class FalsingClassifier {
29     private final FalsingDataProvider mDataProvider;
30 
31     private final FalsingDataProvider.MotionEventListener mMotionEventListener = this::onTouchEvent;
32 
FalsingClassifier(FalsingDataProvider dataProvider)33     FalsingClassifier(FalsingDataProvider dataProvider) {
34         mDataProvider = dataProvider;
35         mDataProvider.addMotionEventListener(mMotionEventListener);
36     }
37 
getFalsingContext()38     protected String getFalsingContext() {
39         return getClass().getSimpleName();
40     }
41 
falsed(double confidence, String reason)42     protected Result falsed(double confidence, String reason) {
43         return Result.falsed(confidence, getFalsingContext(), reason);
44     }
45 
getRecentMotionEvents()46     List<MotionEvent> getRecentMotionEvents() {
47         return mDataProvider.getRecentMotionEvents();
48     }
49 
getPriorMotionEvents()50     List<MotionEvent> getPriorMotionEvents() {
51         return mDataProvider.getPriorMotionEvents();
52     }
53 
getFirstMotionEvent()54     MotionEvent getFirstMotionEvent() {
55         return mDataProvider.getFirstRecentMotionEvent();
56     }
57 
getLastMotionEvent()58     MotionEvent getLastMotionEvent() {
59         return mDataProvider.getLastMotionEvent();
60     }
61 
isHorizontal()62     boolean isHorizontal() {
63         return mDataProvider.isHorizontal();
64     }
65 
isRight()66     boolean isRight() {
67         return mDataProvider.isRight();
68     }
69 
isVertical()70     boolean isVertical() {
71         return mDataProvider.isVertical();
72     }
73 
isUp()74     boolean isUp() {
75         return mDataProvider.isUp();
76     }
77 
getAngle()78     float getAngle() {
79         return mDataProvider.getAngle();
80     }
81 
getWidthPixels()82     int getWidthPixels() {
83         return mDataProvider.getWidthPixels();
84     }
85 
getHeightPixels()86     int getHeightPixels() {
87         return mDataProvider.getHeightPixels();
88     }
89 
getXdpi()90     float getXdpi() {
91         return mDataProvider.getXdpi();
92     }
93 
getYdpi()94     float getYdpi() {
95         return mDataProvider.getYdpi();
96     }
97 
cleanup()98     void cleanup() {
99         mDataProvider.removeMotionEventListener(mMotionEventListener);
100     }
101 
102     /**
103      * Called whenever a MotionEvent occurs.
104      *
105      * Useful for classifiers that need to see every MotionEvent, but most can probably
106      * use {@link #getRecentMotionEvents()} instead, which will return a list of MotionEvents.
107      */
onTouchEvent(MotionEvent motionEvent)108     void onTouchEvent(MotionEvent motionEvent) {}
109 
110     /**
111      * Called when a ProximityEvent occurs (change in near/far).
112      */
onProximityEvent(FalsingManager.ProximityEvent proximityEvent)113     void onProximityEvent(FalsingManager.ProximityEvent proximityEvent) {}
114 
115     /**
116      * The phone screen has turned on and we need to begin falsing detection.
117      */
onSessionStarted()118     void onSessionStarted() {}
119 
120     /**
121      * The phone screen has turned off and falsing data can be discarded.
122      */
onSessionEnded()123     void onSessionEnded() {}
124 
125     /**
126      * Returns whether a gesture looks like a false touch, taking history into consideration.
127      *
128      * See {@link HistoryTracker#falseBelief()} and {@link HistoryTracker#falseConfidence()}.
129      */
classifyGesture( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)130     Result classifyGesture(
131             @Classifier.InteractionType int interactionType,
132             double historyBelief, double historyConfidence) {
133         return calculateFalsingResult(interactionType, historyBelief, historyConfidence);
134     }
135 
136     /**
137      * Calculate a result based on available data.
138      *
139      * When passed a historyConfidence of 0, the history belief should be wholly ignored.
140      */
calculateFalsingResult( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)141     abstract Result calculateFalsingResult(
142             @Classifier.InteractionType int interactionType,
143             double historyBelief, double historyConfidence);
144 
145     /** */
logDebug(String msg)146     public static void logDebug(String msg) {
147         BrightLineFalsingManager.logDebug(msg);
148     }
149 
150     /** */
logVerbose(String msg)151     public static void logVerbose(String msg) {
152         BrightLineFalsingManager.logVerbose(msg);
153     }
154 
155     /** */
logInfo(String msg)156     public static void logInfo(String msg) {
157         BrightLineFalsingManager.logInfo(msg);
158     }
159 
160     /** */
logError(String msg)161     public static void logError(String msg) {
162         BrightLineFalsingManager.logError(msg);
163     }
164 
165     /**
166      * A Falsing result that encapsulates the boolean result along with confidence and a reason.
167      */
168     public static class Result {
169         private final boolean mFalsed;
170         private final double mConfidence;
171         private final String mContext;
172         private final String mReason;
173 
174         /**
175          * See {@link #falsed(double, String, String)} abd {@link #passed(double)}.
176          */
Result(boolean falsed, double confidence, String context, String reason)177         private Result(boolean falsed, double confidence, String context, String reason) {
178             mFalsed = falsed;
179             mConfidence = confidence;
180             mContext = context;
181             mReason = reason;
182         }
183 
isFalse()184         public boolean isFalse() {
185             return mFalsed;
186         }
187 
getConfidence()188         public double getConfidence() {
189             return mConfidence;
190         }
191 
getReason()192         public String getReason() {
193             return String.format("{context=%s reason=%s}", mContext, mReason);
194         }
195 
196         /**
197          * Construct a "falsed" result indicating that a gesture should be treated as accidental.
198          */
falsed(double confidence, String context, String reason)199         public static Result falsed(double confidence, String context, String reason) {
200             return new Result(true, confidence, context, reason);
201         }
202 
203         /**
204          * Construct a "passed" result indicating that a gesture should be allowed.
205          */
passed(double confidence)206         public static Result passed(double confidence) {
207             return new Result(false, confidence, null, null);
208         }
209     }
210 }
211