1/*
2 * Copyright (C) 2016 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
17package com.android.incallui.answerproximitysensor;
18
19import android.content.Context;
20import android.hardware.display.DisplayManager;
21import android.os.PowerManager;
22import android.view.Display;
23import com.android.dialer.common.ConfigProviderBindings;
24import com.android.dialer.common.LogUtil;
25import com.android.incallui.call.DialerCall;
26import com.android.incallui.call.DialerCall.State;
27import com.android.incallui.call.DialerCallListener;
28
29/**
30 * This class prevents users from accidentally answering calls by keeping the screen off until the
31 * proximity sensor is unblocked. If the screen is already on or if this is a call waiting call then
32 * nothing is done.
33 */
34public class AnswerProximitySensor
35    implements DialerCallListener, AnswerProximityWakeLock.ScreenOnListener {
36
37  private static final String CONFIG_ANSWER_PROXIMITY_SENSOR_ENABLED =
38      "answer_proximity_sensor_enabled";
39  private static final String CONFIG_ANSWER_PSEUDO_PROXIMITY_WAKE_LOCK_ENABLED =
40      "answer_pseudo_proximity_wake_lock_enabled";
41
42  private final DialerCall call;
43  private final AnswerProximityWakeLock answerProximityWakeLock;
44
45  public static boolean shouldUse(Context context, DialerCall call) {
46    // Don't use the AnswerProximitySensor for call waiting and other states. Those states are
47    // handled by the general ProximitySensor code.
48    if (call.getState() != State.INCOMING) {
49      LogUtil.i("AnswerProximitySensor.shouldUse", "call state is not incoming");
50      return false;
51    }
52
53    if (!ConfigProviderBindings.get(context)
54        .getBoolean(CONFIG_ANSWER_PROXIMITY_SENSOR_ENABLED, true)) {
55      LogUtil.i("AnswerProximitySensor.shouldUse", "disabled by config");
56      return false;
57    }
58
59    if (!context
60        .getSystemService(PowerManager.class)
61        .isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
62      LogUtil.i("AnswerProximitySensor.shouldUse", "wake lock level not supported");
63      return false;
64    }
65
66    if (isDefaultDisplayOn(context)) {
67      LogUtil.i("AnswerProximitySensor.shouldUse", "display is already on");
68      return false;
69    }
70
71    return true;
72  }
73
74  public AnswerProximitySensor(
75      Context context, DialerCall call, PseudoScreenState pseudoScreenState) {
76    this.call = call;
77
78    LogUtil.i("AnswerProximitySensor.constructor", "acquiring lock");
79    if (ConfigProviderBindings.get(context)
80        .getBoolean(CONFIG_ANSWER_PSEUDO_PROXIMITY_WAKE_LOCK_ENABLED, true)) {
81      answerProximityWakeLock = new PseudoProximityWakeLock(context, pseudoScreenState);
82    } else {
83      // TODO: choose a wake lock implementation base on framework/device.
84      // These bugs requires the PseudoProximityWakeLock workaround:
85      // b/30439151 Proximity sensor not working on M
86      // b/31499931 fautly touch input when screen is off on marlin/sailfish
87      answerProximityWakeLock = new SystemProximityWakeLock(context);
88    }
89    answerProximityWakeLock.setScreenOnListener(this);
90    answerProximityWakeLock.acquire();
91
92    call.addListener(this);
93  }
94
95  private void cleanup() {
96    call.removeListener(this);
97    releaseProximityWakeLock();
98  }
99
100  private void releaseProximityWakeLock() {
101    if (answerProximityWakeLock.isHeld()) {
102      LogUtil.i("AnswerProximitySensor.releaseProximityWakeLock", "releasing lock");
103      answerProximityWakeLock.release();
104    }
105  }
106
107  private static boolean isDefaultDisplayOn(Context context) {
108    Display display =
109        context.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY);
110    return display.getState() == Display.STATE_ON;
111  }
112
113  @Override
114  public void onDialerCallDisconnect() {
115    LogUtil.i("AnswerProximitySensor.onDialerCallDisconnect", null);
116    cleanup();
117  }
118
119  @Override
120  public void onDialerCallUpdate() {
121    if (call.getState() != State.INCOMING) {
122      LogUtil.i("AnswerProximitySensor.onDialerCallUpdate", "no longer incoming, cleaning up");
123      cleanup();
124    }
125  }
126
127  @Override
128  public void onDialerCallChildNumberChange() {}
129
130  @Override
131  public void onDialerCallLastForwardedNumberChange() {}
132
133  @Override
134  public void onDialerCallUpgradeToVideo() {}
135
136  @Override
137  public void onWiFiToLteHandover() {}
138
139  @Override
140  public void onHandoverToWifiFailure() {}
141
142  @Override
143  public void onInternationalCallOnWifi() {}
144
145  @Override
146  public void onDialerCallSessionModificationStateChange() {}
147
148  @Override
149  public void onScreenOn() {
150    cleanup();
151  }
152}
153