10e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski/*
20e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * Copyright (C) 2015 The Android Open Source Project
30e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski *
40e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * Licensed under the Apache License, Version 2.0 (the "License");
50e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * you may not use this file except in compliance with the License.
60e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * You may obtain a copy of the License at
70e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski *
80e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski *      http://www.apache.org/licenses/LICENSE-2.0
90e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski *
100e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * Unless required by applicable law or agreed to in writing, software
110e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * distributed under the License is distributed on an "AS IS" BASIS,
120e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * See the License for the specific language governing permissions and
140e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * limitations under the License
150e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski */
160e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
170e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskipackage com.android.systemui.classifier;
180e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
190e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.content.Context;
200e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.database.ContentObserver;
210e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.hardware.Sensor;
220e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.hardware.SensorEvent;
230e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.hardware.SensorEventListener;
240e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.hardware.SensorManager;
250e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.os.Handler;
26c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roosimport android.os.PowerManager;
270e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.os.UserHandle;
280e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.provider.Settings;
290e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport android.view.MotionEvent;
30ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roosimport android.view.accessibility.AccessibilityManager;
310e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
320e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport com.android.systemui.analytics.DataCollector;
330e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskiimport com.android.systemui.statusbar.StatusBarState;
340e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
35401caaedd871894620accc1d14592c08095b5523Adrian Roosimport java.io.PrintWriter;
36401caaedd871894620accc1d14592c08095b5523Adrian Roos
370e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski/**
380e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * When the phone is locked, listens to touch, sensor and phone events and sends them to
390e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * DataCollector and HumanInteractionClassifier.
400e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski *
410e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski * It does not collect touch events when the bouncer shows up.
420e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski */
430e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowskipublic class FalsingManager implements SensorEventListener {
440e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
450e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
466dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    private static final int[] CLASSIFIER_SENSORS = new int[] {
476dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski            Sensor.TYPE_PROXIMITY,
486dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    };
496dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski
506dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    private static final int[] COLLECTOR_SENSORS = new int[] {
510e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            Sensor.TYPE_ACCELEROMETER,
520e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            Sensor.TYPE_GYROSCOPE,
530e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            Sensor.TYPE_PROXIMITY,
540e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            Sensor.TYPE_LIGHT,
550e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            Sensor.TYPE_ROTATION_VECTOR,
560e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    };
570e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
580e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private final Handler mHandler = new Handler();
590e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private final Context mContext;
600e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
610e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private final SensorManager mSensorManager;
620e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private final DataCollector mDataCollector;
630e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private final HumanInteractionClassifier mHumanInteractionClassifier;
64ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos    private final AccessibilityManager mAccessibilityManager;
650e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
660e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private static FalsingManager sInstance = null;
670e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
680e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private boolean mEnforceBouncer = false;
690e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private boolean mBouncerOn = false;
700e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private boolean mSessionActive = false;
710e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private int mState = StatusBarState.SHADE;
72c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos    private boolean mScreenOn;
730e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
740e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
750e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        @Override
760e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        public void onChange(boolean selfChange) {
770e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            updateConfiguration();
780e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
790e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    };
800e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
810e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private FalsingManager(Context context) {
820e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mContext = context;
83ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos        mSensorManager = mContext.getSystemService(SensorManager.class);
84ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
850e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector = DataCollector.getInstance(mContext);
860e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
87c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
880e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
890e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mContext.getContentResolver().registerContentObserver(
900e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski                Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
910e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski                mSettingsObserver,
920e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski                UserHandle.USER_ALL);
930e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
940e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        updateConfiguration();
950e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
960e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
970e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public static FalsingManager getInstance(Context context) {
980e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        if (sInstance == null) {
990e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            sInstance = new FalsingManager(context);
1000e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
1010e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        return sInstance;
1020e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1030e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
1040e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private void updateConfiguration() {
1050e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
1060e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski                ENFORCE_BOUNCER, 0);
1070e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1080e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
109c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos    private boolean shouldSessionBeActive() {
110401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED && FalsingLog.VERBOSE)
111401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.v("shouldBeActive", new StringBuilder()
112401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("enabled=").append(isEnabled() ? 1 : 0)
113401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
114401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append(" mState=").append(StatusBarState.toShortString(mState))
115401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString()
116401caaedd871894620accc1d14592c08095b5523Adrian Roos            );
117401caaedd871894620accc1d14592c08095b5523Adrian Roos        return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD);
118c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos    }
119c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos
1200e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private boolean sessionEntrypoint() {
121c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        if (!mSessionActive && shouldSessionBeActive()) {
1220e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            onSessionStart();
1230e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            return true;
1240e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
1250e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        return false;
1260e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1270e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
128c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos    private void sessionExitpoint(boolean force) {
129c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        if (mSessionActive && (force || !shouldSessionBeActive())) {
1300e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mSessionActive = false;
1310e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mSensorManager.unregisterListener(this);
1320e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
1330e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1340e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
1350e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private void onSessionStart() {
136401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
137401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
138401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
1390e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mBouncerOn = false;
1400e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mSessionActive = true;
1416dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski
1426dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski        if (mHumanInteractionClassifier.isEnabled()) {
1436dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski            registerSensors(CLASSIFIER_SENSORS);
1446dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski        }
1456dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski        if (mDataCollector.isEnabled()) {
1466dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski            registerSensors(COLLECTOR_SENSORS);
1476dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski        }
1486dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    }
1496dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski
1506dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    private void registerSensors(int [] sensors) {
1516dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski        for (int sensorType : sensors) {
1520e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            Sensor s = mSensorManager.getDefaultSensor(sensorType);
1530e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            if (s != null) {
1540e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski                mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
1550e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            }
1560e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
1570e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1580e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
1596dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    public boolean isClassiferEnabled() {
1606dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski        return mHumanInteractionClassifier.isEnabled();
1616dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski    }
1626dc59b48c040cf6871d7ff6d8e5242b1a5ddd032Blazej Magnowski
1630e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    private boolean isEnabled() {
1640e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
1650e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1660e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
1670e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    /**
1680e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski     * @return true if the classifier determined that this is not a human interacting with the phone
1690e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski     */
1709f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski    public boolean isFalseTouch() {
171401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
1726a04cb1efc4a6cfd6733d92c0c7efca5f9082e5dAdrian Roos            // We're getting some false wtfs from touches that happen after the device went
1736a04cb1efc4a6cfd6733d92c0c7efca5f9082e5dAdrian Roos            // to sleep. Only report missing sessions that happen when the device is interactive.
1746a04cb1efc4a6cfd6733d92c0c7efca5f9082e5dAdrian Roos            if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()) {
175401caaedd871894620accc1d14592c08095b5523Adrian Roos                FalsingLog.wtf("isFalseTouch", new StringBuilder()
176401caaedd871894620accc1d14592c08095b5523Adrian Roos                        .append("Session is not active, yet there's a query for a false touch.")
177401caaedd871894620accc1d14592c08095b5523Adrian Roos                        .append(" enabled=").append(isEnabled() ? 1 : 0)
178401caaedd871894620accc1d14592c08095b5523Adrian Roos                        .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
179401caaedd871894620accc1d14592c08095b5523Adrian Roos                        .append(" mState=").append(StatusBarState.toShortString(mState))
180401caaedd871894620accc1d14592c08095b5523Adrian Roos                        .toString());
181401caaedd871894620accc1d14592c08095b5523Adrian Roos            }
182401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
183ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos        if (mAccessibilityManager.isTouchExplorationEnabled()) {
184ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos            // Touch exploration triggers false positives in the classifier and
185ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos            // already sufficiently prevents false unlocks.
186ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos            return false;
187ca664b93b91ee4c29b22e914c01411c241f5068eAdrian Roos        }
1889f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        return mHumanInteractionClassifier.isFalseTouch();
1890e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1900e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
1910e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    @Override
1920e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public synchronized void onSensorChanged(SensorEvent event) {
1930e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onSensorChanged(event);
1940e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mHumanInteractionClassifier.onSensorChanged(event);
1950e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
1960e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
1970e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    @Override
1980e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onAccuracyChanged(Sensor sensor, int accuracy) {
1990e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onAccuracyChanged(sensor, accuracy);
2000e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2010e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2020e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public boolean shouldEnforceBouncer() {
2030e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        return mEnforceBouncer;
2040e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2050e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2060e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void setStatusBarState(int state) {
207401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
208401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("setStatusBarState", new StringBuilder()
209401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("from=").append(StatusBarState.toShortString(mState))
210401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append(" to=").append(StatusBarState.toShortString(state))
211401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString());
212401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
2130e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mState = state;
214c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        if (shouldSessionBeActive()) {
215c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos            sessionEntrypoint();
216c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        } else {
217c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos            sessionExitpoint(false /* force */);
218c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        }
2190e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2200e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2210e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onScreenTurningOn() {
222401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
223401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onScreenTurningOn", new StringBuilder()
224401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("from=").append(mScreenOn ? 1 : 0)
225401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString());
226401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
227c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        mScreenOn = true;
2280e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        if (sessionEntrypoint()) {
2290e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mDataCollector.onScreenTurningOn();
2300e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
2310e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2320e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2330e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onScreenOnFromTouch() {
234401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
235401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
236401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("from=").append(mScreenOn ? 1 : 0)
237401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString());
238401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
239c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        mScreenOn = true;
2400e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        if (sessionEntrypoint()) {
2410e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mDataCollector.onScreenOnFromTouch();
2420e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
2430e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2440e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2450e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onScreenOff() {
246401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
247401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onScreenOff", new StringBuilder()
248401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("from=").append(mScreenOn ? 1 : 0)
249401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString());
250401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
2510e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onScreenOff();
252c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        mScreenOn = false;
253c5584ceaad422c96f978132aee4d21ee1f61fc7dAdrian Roos        sessionExitpoint(false /* force */);
2540e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2550e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2560e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onSucccessfulUnlock() {
257401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
258401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onSucccessfulUnlock", "");
259401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
2600e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onSucccessfulUnlock();
2610e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2620e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2630e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onBouncerShown() {
264401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
265401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onBouncerShown", new StringBuilder()
266401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("from=").append(mBouncerOn ? 1 : 0)
267401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString());
268401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
2690e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        if (!mBouncerOn) {
2700e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mBouncerOn = true;
2710e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mDataCollector.onBouncerShown();
2720e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
2730e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2740e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2750e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onBouncerHidden() {
276401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
277401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onBouncerHidden", new StringBuilder()
278401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .append("from=").append(mBouncerOn ? 1 : 0)
279401caaedd871894620accc1d14592c08095b5523Adrian Roos                    .toString());
280401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
2810e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        if (mBouncerOn) {
2820e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mBouncerOn = false;
2830e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mDataCollector.onBouncerHidden();
2840e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
2850e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2860e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2870e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onQsDown() {
288401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
289401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onQsDown", "");
290401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
2919f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
2920e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onQsDown();
2930e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2940e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2950e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void setQsExpanded(boolean expanded) {
2960e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.setQsExpanded(expanded);
2970e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
2980e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
2990e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onTrackingStarted() {
300401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
301401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onTrackingStarted", "");
302401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
3039f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        mHumanInteractionClassifier.setType(Classifier.UNLOCK);
3040e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onTrackingStarted();
3050e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3060e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3070e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onTrackingStopped() {
3080e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onTrackingStopped();
3090e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3100e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3110e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificationActive() {
3120e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificationActive();
3130e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3140e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3150e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificationDoubleTap() {
3160e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificationDoubleTap();
3170e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3180e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3190e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void setNotificationExpanded() {
3200e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.setNotificationExpanded();
3210e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3220e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3230e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificatonStartDraggingDown() {
324401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
325401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onNotificatonStartDraggingDown", "");
326401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
3279f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
3280e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificatonStartDraggingDown();
3290e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3300e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3310e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificatonStopDraggingDown() {
3320e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificatonStopDraggingDown();
3330e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3340e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3350e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificationDismissed() {
3360e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificationDismissed();
3370e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3380e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3390e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificatonStartDismissing() {
340401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
341401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onNotificatonStartDismissing", "");
342401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
3439f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
3440e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificatonStartDismissing();
3450e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3460e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3470e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onNotificatonStopDismissing() {
3480e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onNotificatonStopDismissing();
3490e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3500e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3510e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onCameraOn() {
3520e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onCameraOn();
3530e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3540e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3550e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onLeftAffordanceOn() {
3560e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onLeftAffordanceOn();
3570e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3580e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3590e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onAffordanceSwipingStarted(boolean rightCorner) {
360401caaedd871894620accc1d14592c08095b5523Adrian Roos        if (FalsingLog.ENABLED) {
361401caaedd871894620accc1d14592c08095b5523Adrian Roos            FalsingLog.i("onAffordanceSwipingStarted", "");
362401caaedd871894620accc1d14592c08095b5523Adrian Roos        }
3639f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        if (rightCorner) {
3649f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
3659f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        } else {
3669f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski            mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
3679f01c5bfa5c1c63e350808c154adfc2953949b15Blazej Magnowski        }
3680e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onAffordanceSwipingStarted(rightCorner);
3690e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3700e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3710e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onAffordanceSwipingAborted() {
3720e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onAffordanceSwipingAborted();
3730e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3740e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3750e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onUnlockHintStarted() {
3760e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onUnlockHintStarted();
3770e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3780e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3790e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onCameraHintStarted() {
3800e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onCameraHintStarted();
3810e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3820e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3830e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onLeftAffordanceHintStarted() {
3840e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        mDataCollector.onLeftAffordanceHintStarted();
3850e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
3860e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski
3870e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    public void onTouchEvent(MotionEvent event, int width, int height) {
3880e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        if (mSessionActive && !mBouncerOn) {
3890e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mDataCollector.onTouchEvent(event, width, height);
3900e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski            mHumanInteractionClassifier.onTouchEvent(event);
3910e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski        }
3920e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski    }
393401caaedd871894620accc1d14592c08095b5523Adrian Roos
394401caaedd871894620accc1d14592c08095b5523Adrian Roos    public void dump(PrintWriter pw) {
395401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.println("FALSING MANAGER");
396401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.print("classifierEnabled="); pw.println(isClassiferEnabled() ? 1 : 0);
397401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
398401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
399401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
400401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
401401caaedd871894620accc1d14592c08095b5523Adrian Roos        pw.println();
402401caaedd871894620accc1d14592c08095b5523Adrian Roos    }
4030e2ffbd48bbedf47deb7f6aed96bd07e2fc96f53Blazej Magnowski}
404