Merge "Hook up emergency metrics"
diff --git a/res/values/config.xml b/res/values/config.xml
index 8c84688..c0e9669 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -60,4 +60,13 @@
between repeats of the ringtone.
When false, the ringtone will be looping with no pause. -->
<bool name="should_pause_between_ringtone_repeats">true</bool>
+
+ <!-- Threshold for the X+Y component of gravity needed for the device orientation to be
+ classified as being on a user's ear. -->
+ <item name="device_on_ear_xy_gravity_threshold" format="float" type="dimen">5.5</item>
+
+ <!-- Lower threshold for the Y-component of gravity needed for the device orientation to be
+ classified as being on a user's ear. If the Y-component is less than this negative value,
+ the device is probably upside-down and therefore not on a ear -->
+ <item name="device_on_ear_y_gravity_negative_threshold" format="float" type="dimen">-1</item>
</resources>
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 2ae7b2a..a8f2bc8 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -446,9 +446,9 @@
}
@VisibleForTesting
- public void startCallWaiting() {
+ public void startCallWaiting(String reason) {
if (mRingingCalls.size() == 1) {
- mRinger.startCallWaiting(mRingingCalls.iterator().next());
+ mRinger.startCallWaiting(mRingingCalls.iterator().next(), reason);
}
}
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index 92e2973..42a1d36 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -30,8 +30,9 @@
public class CallAudioModeStateMachine extends StateMachine {
public static class Factory {
- public CallAudioModeStateMachine create(AudioManager am) {
- return new CallAudioModeStateMachine(am);
+ public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper,
+ AudioManager am) {
+ return new CallAudioModeStateMachine(systemStateHelper, am);
}
}
@@ -333,7 +334,7 @@
return HANDLED;
case NEW_RINGING_CALL:
// Don't make a call ring over an active call, but do play a call waiting tone.
- mCallAudioManager.startCallWaiting();
+ mCallAudioManager.startCallWaiting("call already active");
return HANDLED;
case NEW_HOLDING_CALL:
// Don't do anything now. Putting an active call on hold will be handled when
@@ -388,7 +389,7 @@
return HANDLED;
case NEW_RINGING_CALL:
// Don't make a call ring over an active call, but do play a call waiting tone.
- mCallAudioManager.startCallWaiting();
+ mCallAudioManager.startCallWaiting("call already active");
return HANDLED;
case NEW_HOLDING_CALL:
// Don't do anything now. Putting an active call on hold will be handled when
@@ -442,8 +443,14 @@
? mVoipCallFocusState : mSimCallFocusState);
return HANDLED;
case NEW_RINGING_CALL:
- // Apparently this is current behavior. Should this be the case?
- transitionTo(mRingingFocusState);
+ // TODO: consider whether to move this into MessageArgs if more things start
+ // to use it.
+ if (args.hasHoldingCalls && mSystemStateHelper.isDeviceAtEar()) {
+ mCallAudioManager.startCallWaiting(
+ "Device is at ear with held call");
+ } else {
+ transitionTo(mRingingFocusState);
+ }
return HANDLED;
case NEW_HOLDING_CALL:
// Do nothing.
@@ -470,14 +477,17 @@
private final BaseState mOtherFocusState = new OtherFocusState();
private final AudioManager mAudioManager;
+ private final SystemStateHelper mSystemStateHelper;
private CallAudioManager mCallAudioManager;
private int mMostRecentMode;
private boolean mIsInitialized = false;
- public CallAudioModeStateMachine(AudioManager audioManager) {
+ public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
+ AudioManager audioManager) {
super(CallAudioModeStateMachine.class.getSimpleName());
mAudioManager = audioManager;
+ mSystemStateHelper = systemStateHelper;
mMostRecentMode = AudioManager.MODE_NORMAL;
addState(mUnfocusedState);
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index f702e1f..941259b 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -360,7 +360,7 @@
CallAudioManager.AudioServiceFactory audioServiceFactory,
BluetoothRouteManager bluetoothManager,
WiredHeadsetManager wiredHeadsetManager,
- SystemStateProvider systemStateProvider,
+ SystemStateHelper systemStateHelper,
DefaultDialerCache defaultDialerCache,
Timeouts.Adapter timeoutsAdapter,
AsyncRingtonePlayer asyncRingtonePlayer,
@@ -422,7 +422,7 @@
RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
SystemVibrator systemVibrator = new SystemVibrator(context);
mInCallController = inCallControllerFactory.create(context, mLock, this,
- systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
+ systemStateHelper, defaultDialerCache, mTimeoutsAdapter,
emergencyCallHelper);
mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
ringtoneFactory, systemVibrator,
@@ -430,8 +430,8 @@
mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext,
(AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), mLock);
mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
- this, callAudioModeStateMachineFactory.create((AudioManager)
- mContext.getSystemService(Context.AUDIO_SERVICE)),
+ this, callAudioModeStateMachineFactory.create(systemStateHelper,
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)),
playerFactory, mRinger, new RingbackPlayer(playerFactory),
bluetoothStateReceiver, mDtmfLocalTonePlayer);
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 6f3c4d5..a413814 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -47,7 +47,7 @@
// TODO: Needed for move to system service: import com.android.internal.R;
import com.android.internal.telecom.IInCallService;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.telecom.SystemStateProvider.SystemStateListener;
+import com.android.server.telecom.SystemStateHelper.SystemStateListener;
import java.util.ArrayList;
import java.util.Collection;
@@ -729,7 +729,7 @@
private final Context mContext;
private final TelecomSystem.SyncRoot mLock;
private final CallsManager mCallsManager;
- private final SystemStateProvider mSystemStateProvider;
+ private final SystemStateHelper mSystemStateHelper;
private final Timeouts.Adapter mTimeoutsAdapter;
private final DefaultDialerCache mDefaultDialerCache;
private final EmergencyCallHelper mEmergencyCallHelper;
@@ -737,13 +737,13 @@
private NonUIInCallServiceConnectionCollection mNonUIInCallServiceConnections;
public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
- SystemStateProvider systemStateProvider,
+ SystemStateHelper systemStateHelper,
DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
EmergencyCallHelper emergencyCallHelper) {
mContext = context;
mLock = lock;
mCallsManager = callsManager;
- mSystemStateProvider = systemStateProvider;
+ mSystemStateHelper = systemStateHelper;
mTimeoutsAdapter = timeoutsAdapter;
mDefaultDialerCache = defaultDialerCache;
mEmergencyCallHelper = emergencyCallHelper;
@@ -753,7 +753,7 @@
resources.getString(R.string.ui_default_package),
resources.getString(R.string.incall_default_class));
- mSystemStateProvider.addListener(mSystemStateListener);
+ mSystemStateHelper.addListener(mSystemStateListener);
}
@Override
@@ -1227,7 +1227,7 @@
}
private boolean shouldUseCarModeUI() {
- return mSystemStateProvider.isCarMode();
+ return mSystemStateHelper.isCarMode();
}
/**
diff --git a/src/com/android/server/telecom/InCallControllerFactory.java b/src/com/android/server/telecom/InCallControllerFactory.java
index e384af7..c3a7831 100644
--- a/src/com/android/server/telecom/InCallControllerFactory.java
+++ b/src/com/android/server/telecom/InCallControllerFactory.java
@@ -23,6 +23,6 @@
*/
public interface InCallControllerFactory {
InCallController create(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
- SystemStateProvider systemStateProvider, DefaultDialerCache defaultDialerCache,
+ SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache,
Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper);
}
diff --git a/src/com/android/server/telecom/QuickResponseUtils.java b/src/com/android/server/telecom/QuickResponseUtils.java
index 5f8269d..84d2636 100644
--- a/src/com/android/server/telecom/QuickResponseUtils.java
+++ b/src/com/android/server/telecom/QuickResponseUtils.java
@@ -117,4 +117,48 @@
Log.d(LOG_TAG, "maybeMigrateLegacyQuickResponses() - Done.");
return;
}
+
+ /**
+ * Determine if the user has changed any of the quick responses back to exactly the same text as
+ * the default text. If they did, clear the preference so we'll rely on the default value and
+ * still be able to re-translate automatically when language changes occur.
+ *
+ * @param context The current context.
+ * @param prefs The quick response shared prefs.
+ */
+ public static void maybeResetQuickResponses(Context context, SharedPreferences prefs) {
+ final Resources res = context.getResources();
+
+ String defaultResponse1 = res.getString(R.string.respond_via_sms_canned_response_1);
+ String currentValue1 = prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_1, "");
+ if (currentValue1.equals(defaultResponse1)) {
+ prefs.edit().remove(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_1).apply();
+ Log.i(QuickResponseUtils.class,
+ "maybeResetQuickResponses: response 1 is identical to default; clear pref.");
+ }
+
+ String defaultResponse2 = res.getString(R.string.respond_via_sms_canned_response_2);
+ String currentValue2 = prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_2, "");
+ if (currentValue2.equals(defaultResponse2)) {
+ prefs.edit().remove(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_2).apply();
+ Log.i(QuickResponseUtils.class,
+ "maybeResetQuickResponses: response 2 is identical to default; clear pref.");
+ }
+
+ String defaultResponse3 = res.getString(R.string.respond_via_sms_canned_response_3);
+ String currentValue3 = prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_3, "");
+ if (currentValue3.equals(defaultResponse3)) {
+ prefs.edit().remove(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_3).apply();
+ Log.i(QuickResponseUtils.class,
+ "maybeResetQuickResponses: response 3 is identical to default; clear pref.");
+ }
+
+ String defaultResponse4 = res.getString(R.string.respond_via_sms_canned_response_4);
+ String currentValue4 = prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_4, "");
+ if (currentValue4.equals(defaultResponse4)) {
+ prefs.edit().remove(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_4).apply();
+ Log.i(QuickResponseUtils.class,
+ "maybeResetQuickResponses: response 4 is identical to default; clear pref.");
+ }
+ }
}
diff --git a/src/com/android/server/telecom/RespondViaSmsManager.java b/src/com/android/server/telecom/RespondViaSmsManager.java
index 964f6ad..fadc6b5 100644
--- a/src/com/android/server/telecom/RespondViaSmsManager.java
+++ b/src/com/android/server/telecom/RespondViaSmsManager.java
@@ -105,6 +105,11 @@
final ArrayList<String> textMessages = new ArrayList<>(
QuickResponseUtils.NUM_CANNED_RESPONSES);
+ // Where the user has changed a quick response back to the same text as the
+ // original text, clear the shared pref. This ensures we always load the resource
+ // in the current active language.
+ QuickResponseUtils.maybeResetQuickResponses(context, prefs);
+
// Note the default values here must agree with the corresponding
// android:defaultValue attributes in respond_via_sms_settings.xml.
textMessages.add(0, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_1,
diff --git a/src/com/android/server/telecom/RespondViaSmsSettings.java b/src/com/android/server/telecom/RespondViaSmsSettings.java
index 2ea204a..3bee5f7 100644
--- a/src/com/android/server/telecom/RespondViaSmsSettings.java
+++ b/src/com/android/server/telecom/RespondViaSmsSettings.java
@@ -54,6 +54,7 @@
getPreferenceManager().setSharedPreferencesName(QuickResponseUtils.SHARED_PREFERENCES_NAME);
mPrefs = getPreferenceManager().getSharedPreferences();
+ QuickResponseUtils.maybeResetQuickResponses(this, mPrefs);
}
@Override
@@ -108,6 +109,9 @@
SharedPreferences.Editor editor = mPrefs.edit();
editor.putString(pref.getKey(), (String) newValue).commit();
+ // If the user just reset the quick response to its original text, clear the pref.
+ QuickResponseUtils.maybeResetQuickResponses(this, mPrefs);
+
return true; // means it's OK to update the state of the Preference with the new value
}
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 51976e1..ee423da 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -233,6 +233,10 @@
}
public void startCallWaiting(Call call) {
+ startCallWaiting(call, null);
+ }
+
+ public void startCallWaiting(Call call, String reason) {
if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
return;
}
@@ -252,7 +256,7 @@
stopRinging();
if (mCallWaitingPlayer == null) {
- Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE);
+ Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE, reason);
mCallWaitingCall = call;
mCallWaitingPlayer =
mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index f15570b..cf5407d 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -39,7 +39,7 @@
* Subclasses supply the service intent and component name and this class will invoke protected
* methods when the class is bound, unbound, or upon failure.
*/
-abstract class ServiceBinder {
+public abstract class ServiceBinder {
/**
* Callback to notify after a binding succeeds or fails.
diff --git a/src/com/android/server/telecom/SystemStateHelper.java b/src/com/android/server/telecom/SystemStateHelper.java
new file mode 100644
index 0000000..69a46c6
--- /dev/null
+++ b/src/com/android/server/telecom/SystemStateHelper.java
@@ -0,0 +1,210 @@
+/*
+ * 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.server.telecom;
+
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.telecom.Log;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Provides various system states to the rest of the telecom codebase.
+ */
+public class SystemStateHelper {
+ public static interface SystemStateListener {
+ public void onCarModeChanged(boolean isCarMode);
+ }
+
+ private final Context mContext;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.startSession("SSP.oR");
+ try {
+ String action = intent.getAction();
+ if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
+ onEnterCarMode();
+ } else if (UiModeManager.ACTION_EXIT_CAR_MODE.equals(action)) {
+ onExitCarMode();
+ } else {
+ Log.w(this, "Unexpected intent received: %s", intent.getAction());
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+ };
+
+ private Set<SystemStateListener> mListeners = new CopyOnWriteArraySet<>();
+ private boolean mIsCarMode;
+
+ public SystemStateHelper(Context context) {
+ mContext = context;
+
+ IntentFilter intentFilter = new IntentFilter(UiModeManager.ACTION_ENTER_CAR_MODE);
+ intentFilter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ Log.i(this, "Registering car mode receiver: %s", intentFilter);
+
+ mIsCarMode = getSystemCarMode();
+ }
+
+ public void addListener(SystemStateListener listener) {
+ if (listener != null) {
+ mListeners.add(listener);
+ }
+ }
+
+ public boolean removeListener(SystemStateListener listener) {
+ return mListeners.remove(listener);
+ }
+
+ public boolean isCarMode() {
+ return mIsCarMode;
+ }
+
+ public boolean isDeviceAtEar() {
+ return isDeviceAtEar(mContext);
+ }
+
+ /**
+ * Returns a guess whether the phone is up to the user's ear. Use the proximity sensor and
+ * the gravity sensor to make a guess
+ * @return true if the proximity sensor is activated, the magnitude of gravity in directions
+ * parallel to the screen is greater than some configurable threshold, and the
+ * y-component of gravity isn't less than some other configurable threshold.
+ */
+ public static boolean isDeviceAtEar(Context context) {
+ SensorManager sm = context.getSystemService(SensorManager.class);
+ if (sm == null) {
+ return false;
+ }
+ Sensor grav = sm.getDefaultSensor(Sensor.TYPE_GRAVITY);
+ Sensor proximity = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (grav == null || proximity == null) {
+ return false;
+ }
+
+ AtomicBoolean result = new AtomicBoolean(true);
+ CountDownLatch gravLatch = new CountDownLatch(1);
+ CountDownLatch proxLatch = new CountDownLatch(1);
+
+ final double xyGravityThreshold = context.getResources().getFloat(
+ R.dimen.device_on_ear_xy_gravity_threshold);
+ final double yGravityNegativeThreshold = context.getResources().getFloat(
+ R.dimen.device_on_ear_y_gravity_negative_threshold);
+
+ SensorEventListener listener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() == Sensor.TYPE_GRAVITY) {
+ if (gravLatch.getCount() == 0) {
+ return;
+ }
+ double xyMag = Math.sqrt(event.values[0] * event.values[0]
+ + event.values[1] * event.values[1]);
+ if (xyMag < xyGravityThreshold
+ || event.values[1] < yGravityNegativeThreshold) {
+ result.set(false);
+ }
+ gravLatch.countDown();
+ } else if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+ if (proxLatch.getCount() == 0) {
+ return;
+ }
+ if (event.values[0] >= proximity.getMaximumRange()) {
+ result.set(false);
+ }
+ proxLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ try {
+ sm.registerListener(listener, grav, SensorManager.SENSOR_DELAY_FASTEST);
+ sm.registerListener(listener, proximity, SensorManager.SENSOR_DELAY_FASTEST);
+ boolean accelValid = gravLatch.await(100, TimeUnit.MILLISECONDS);
+ boolean proxValid = proxLatch.await(100, TimeUnit.MILLISECONDS);
+ if (accelValid && proxValid) {
+ return result.get();
+ } else {
+ Log.w(SystemStateHelper.class.getSimpleName(),
+ "Timed out waiting for sensors: %b %b", accelValid, proxValid);
+ return false;
+ }
+ } catch (InterruptedException e) {
+ return false;
+ } finally {
+ sm.unregisterListener(listener);
+ }
+ }
+
+ private void onEnterCarMode() {
+ if (!mIsCarMode) {
+ Log.i(this, "Entering carmode");
+ mIsCarMode = true;
+ notifyCarMode();
+ }
+ }
+
+ private void onExitCarMode() {
+ if (mIsCarMode) {
+ Log.i(this, "Exiting carmode");
+ mIsCarMode = false;
+ notifyCarMode();
+ }
+ }
+
+ private void notifyCarMode() {
+ for (SystemStateListener listener : mListeners) {
+ listener.onCarModeChanged(mIsCarMode);
+ }
+ }
+
+ /**
+ * Checks the system for the current car mode.
+ *
+ * @return True if in car mode, false otherwise.
+ */
+ private boolean getSystemCarMode() {
+ UiModeManager uiModeManager =
+ (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+
+ if (uiModeManager != null) {
+ return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/server/telecom/SystemStateProvider.java b/src/com/android/server/telecom/SystemStateProvider.java
deleted file mode 100644
index e1938b1..0000000
--- a/src/com/android/server/telecom/SystemStateProvider.java
+++ /dev/null
@@ -1,124 +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.server.telecom;
-
-import android.app.UiModeManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.telecom.Log;
-
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-/**
- * Provides various system states to the rest of the telecom codebase. So far, that's only car-mode.
- */
-public class SystemStateProvider {
-
- public static interface SystemStateListener {
- public void onCarModeChanged(boolean isCarMode);
- }
-
- private final Context mContext;
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.startSession("SSP.oR");
- try {
- String action = intent.getAction();
- if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
- onEnterCarMode();
- } else if (UiModeManager.ACTION_EXIT_CAR_MODE.equals(action)) {
- onExitCarMode();
- } else {
- Log.w(this, "Unexpected intent received: %s", intent.getAction());
- }
- } finally {
- Log.endSession();
- }
- }
- };
-
- private Set<SystemStateListener> mListeners = new CopyOnWriteArraySet<>();
- private boolean mIsCarMode;
-
- public SystemStateProvider(Context context) {
- mContext = context;
-
- IntentFilter intentFilter = new IntentFilter(UiModeManager.ACTION_ENTER_CAR_MODE);
- intentFilter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- Log.i(this, "Registering car mode receiver: %s", intentFilter);
-
- mIsCarMode = getSystemCarMode();
- }
-
- public void addListener(SystemStateListener listener) {
- if (listener != null) {
- mListeners.add(listener);
- }
- }
-
- public boolean removeListener(SystemStateListener listener) {
- return mListeners.remove(listener);
- }
-
- public boolean isCarMode() {
- return mIsCarMode;
- }
-
- private void onEnterCarMode() {
- if (!mIsCarMode) {
- Log.i(this, "Entering carmode");
- mIsCarMode = true;
- notifyCarMode();
- }
- }
-
- private void onExitCarMode() {
- if (mIsCarMode) {
- Log.i(this, "Exiting carmode");
- mIsCarMode = false;
- notifyCarMode();
- }
- }
-
- private void notifyCarMode() {
- for (SystemStateListener listener : mListeners) {
- listener.onCarModeChanged(mIsCarMode);
- }
- }
-
- /**
- * Checks the system for the current car mode.
- *
- * @return True if in car mode, false otherwise.
- */
- private boolean getSystemCarMode() {
- UiModeManager uiModeManager =
- (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
-
- if (uiModeManager != null) {
- return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
- }
-
- return false;
- }
-}
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 5a4b328..81d1700 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -47,7 +47,6 @@
import android.telecom.VideoProfile;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
import android.util.EventLog;
import com.android.internal.telecom.ITelecomService;
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index bb7f3fb..6fa7167 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -238,7 +238,7 @@
mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
- SystemStateProvider systemStateProvider = new SystemStateProvider(mContext);
+ SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
mMissedCallNotifier = missedCallNotifierImplFactory
.makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar, defaultDialerCache);
@@ -249,7 +249,7 @@
InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
@Override
public InCallController create(Context context, SyncRoot lock,
- CallsManager callsManager, SystemStateProvider systemStateProvider,
+ CallsManager callsManager, SystemStateHelper systemStateProvider,
DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
EmergencyCallHelper emergencyCallHelper) {
return new InCallController(context, lock, callsManager, systemStateProvider,
@@ -271,7 +271,7 @@
audioServiceFactory,
bluetoothRouteManager,
wiredHeadsetManager,
- systemStateProvider,
+ systemStateHelper,
defaultDialerCache,
timeoutsAdapter,
asyncRingtonePlayer,
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index d98de45..7ab6ab2 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -123,8 +123,9 @@
BluetoothDevice erroneouslyConnectedDevice = getBluetoothAudioConnectedDevice();
if (erroneouslyConnectedDevice != null) {
Log.w(LOG_TAG, "Entering AudioOff state but device %s appears to be connected. " +
- "Disconnecting.", erroneouslyConnectedDevice);
- disconnectAudio();
+ "Switching to audio-on state for %s", erroneouslyConnectedDevice);
+ // change this to just transition to the new audio on state
+ transitionToActualState();
}
cleanupStatesForDisconnectedDevices();
if (mListener != null) {
diff --git a/tests/Android.mk b/tests/Android.mk
index 395d547..c92b125 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -20,7 +20,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-ex-camera2 \
guava \
- mockito-target \
+ mockito-target-inline \
android-support-test \
platform-test-annotations
@@ -51,6 +51,9 @@
LOCAL_USE_AAPT2 := true
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages com.android.server.telecom
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
index 68c5014..56f585f 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
@@ -22,6 +22,7 @@
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallAudioModeStateMachine;
import com.android.server.telecom.CallAudioRouteStateMachine;
+import com.android.server.telecom.SystemStateHelper;
import org.junit.Before;
import org.junit.Test;
@@ -30,9 +31,10 @@
import org.mockito.Mock;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,6 +43,7 @@
public class CallAudioModeStateMachineTest extends TelecomTestCase {
private static final int TEST_TIMEOUT = 1000;
+ @Mock private SystemStateHelper mSystemStateHelper;
@Mock private AudioManager mAudioManager;
@Mock private CallAudioManager mCallAudioManager;
@@ -53,7 +56,8 @@
@SmallTest
@Test
public void testNoFocusWhenRingerSilenced() throws Throwable {
- CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
+ CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
+ mAudioManager);
sm.setCallAudioManager(mCallAudioManager);
sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -84,8 +88,47 @@
@SmallTest
@Test
+ public void testNoRingWhenDeviceIsAtEar() {
+ CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
+ mAudioManager);
+ sm.setCallAudioManager(mCallAudioManager);
+ sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
+ sm.sendMessage(CallAudioModeStateMachine.NEW_HOLDING_CALL,
+ new CallAudioModeStateMachine.MessageArgs(
+ false, // hasActiveOrDialingCalls
+ false, // hasRingingCalls
+ true, // hasHoldingCalls
+ false, // isTonePlaying
+ false, // foregroundCallIsVoip
+ null // session
+ ));
+ waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+ assertEquals(CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, sm.getCurrentStateName());
+ when(mSystemStateHelper.isDeviceAtEar()).thenReturn(true);
+
+ resetMocks();
+ sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL,
+ new CallAudioModeStateMachine.MessageArgs(
+ false, // hasActiveOrDialingCalls
+ true, // hasRingingCalls
+ true, // hasHoldingCalls
+ false, // isTonePlaying
+ false, // foregroundCallIsVoip
+ null // session
+ ));
+ waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+ verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
+ verify(mAudioManager, never()).setMode(anyInt());
+ verify(mCallAudioManager, never()).startRinging();
+ verify(mCallAudioManager).startCallWaiting(nullable(String.class));
+ }
+
+ @SmallTest
+ @Test
public void testRegainFocusWhenHfpIsConnectedSilenced() throws Throwable {
- CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
+ CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
+ mAudioManager);
sm.setCallAudioManager(mCallAudioManager);
sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -128,6 +171,6 @@
private void resetMocks() {
- reset(mCallAudioManager, mAudioManager);
+ clearInvocations(mCallAudioManager, mAudioManager);
}
}
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
index b8b4859..81339ed 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
@@ -16,11 +16,13 @@
package com.android.server.telecom.tests;
+import android.content.Context;
import android.media.AudioManager;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallAudioModeStateMachine;
+import com.android.server.telecom.SystemStateHelper;
import org.junit.Before;
import org.junit.Test;
@@ -35,6 +37,7 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -95,6 +98,7 @@
private static final int TEST_TIMEOUT = 1000;
+ @Mock private SystemStateHelper mSystemStateHelper;
@Mock private AudioManager mAudioManager;
@Mock private CallAudioManager mCallAudioManager;
private final ModeTestParameters mParams;
@@ -112,7 +116,8 @@
@Test
@SmallTest
public void modeTransitionTest() {
- CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
+ CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
+ mAudioManager);
sm.setCallAudioManager(mCallAudioManager);
sm.sendMessage(mParams.initialAudioState);
waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -163,11 +168,11 @@
switch (mParams.expectedCallWaitingInteraction) {
case NO_CHANGE:
- verify(mCallAudioManager, never()).startCallWaiting();
+ verify(mCallAudioManager, never()).startCallWaiting(nullable(String.class));
verify(mCallAudioManager, never()).stopCallWaiting();
break;
case ON:
- verify(mCallAudioManager).startCallWaiting();
+ verify(mCallAudioManager).startCallWaiting(nullable(String.class));
break;
case OFF:
verify(mCallAudioManager).stopCallWaiting();
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index b51794a..bc9cfc3 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -68,7 +68,7 @@
import com.android.server.telecom.PhoneNumberUtilsAdapter;
import com.android.server.telecom.ProximitySensorManager;
import com.android.server.telecom.ProximitySensorManagerFactory;
-import com.android.server.telecom.SystemStateProvider;
+import com.android.server.telecom.SystemStateHelper;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
import com.android.server.telecom.WiredHeadsetManager;
@@ -131,7 +131,7 @@
@Mock private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
@Mock private BluetoothRouteManager mBluetoothRouteManager;
@Mock private WiredHeadsetManager mWiredHeadsetManager;
- @Mock private SystemStateProvider mSystemStateProvider;
+ @Mock private SystemStateHelper mSystemStateHelper;
@Mock private DefaultDialerCache mDefaultDialerCache;
@Mock private Timeouts.Adapter mTimeoutsAdapter;
@Mock private AsyncRingtonePlayer mAsyncRingtonePlayer;
@@ -165,7 +165,7 @@
any())).thenReturn(mInCallController);
when(mCallAudioRouteStateMachineFactory.create(any(), any(), any(), any(), any(), any(),
anyInt())).thenReturn(mCallAudioRouteStateMachine);
- when(mCallAudioModeStateMachineFactory.create(any()))
+ when(mCallAudioModeStateMachineFactory.create(any(), any()))
.thenReturn(mCallAudioModeStateMachine);
when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
when(mClockProxy.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
@@ -184,7 +184,7 @@
mAudioServiceFactory,
mBluetoothRouteManager,
mWiredHeadsetManager,
- mSystemStateProvider,
+ mSystemStateHelper,
mDefaultDialerCache,
mTimeoutsAdapter,
mAsyncRingtonePlayer,
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index af08a6f..3be9594 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -525,6 +525,10 @@
});
}
+ public void putFloatResource(int id, final float value) {
+ when(mResources.getFloat(eq(id))).thenReturn(value);
+ }
+
public void putBooleanResource(int id, boolean value) {
when(mResources.getBoolean(eq(id))).thenReturn(value);
}
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 0671a4e..d76bb6c 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -50,7 +50,7 @@
import com.android.server.telecom.InCallController;
import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.R;
-import com.android.server.telecom.SystemStateProvider;
+import com.android.server.telecom.SystemStateHelper;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
@@ -88,7 +88,7 @@
@Mock CallsManager mMockCallsManager;
@Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar;
@Mock BluetoothHeadsetProxy mMockBluetoothHeadset;
- @Mock SystemStateProvider mMockSystemStateProvider;
+ @Mock SystemStateHelper mMockSystemStateHelper;
@Mock PackageManager mMockPackageManager;
@Mock Call mMockCall;
@Mock Resources mMockResources;
@@ -122,7 +122,7 @@
mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, SYS_PKG,
mTimeoutsAdapter);
mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
- mMockSystemStateProvider, mDefaultDialerCache, mTimeoutsAdapter,
+ mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
mEmergencyCallHelper);
}
diff --git a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
new file mode 100644
index 0000000..efe8796
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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.server.telecom.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.SystemStateHelper;
+import com.android.server.telecom.SystemStateHelper.SystemStateListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.internal.util.reflection.FieldSetter;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Unit tests for SystemStateHelper
+ */
+@RunWith(JUnit4.class)
+public class SystemStateHelperTest extends TelecomTestCase {
+
+ Context mContext;
+ @Mock SystemStateListener mSystemStateListener;
+ @Mock Sensor mGravitySensor;
+ @Mock Sensor mProxSensor;
+ @Mock UiModeManager mUiModeManager;
+ @Mock SensorManager mSensorManager;
+ @Mock Intent mIntentEnter;
+ @Mock Intent mIntentExit;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+ doReturn(mSensorManager).when(mContext).getSystemService(SensorManager.class);
+ when(mGravitySensor.getType()).thenReturn(Sensor.TYPE_GRAVITY);
+ when(mProxSensor.getType()).thenReturn(Sensor.TYPE_PROXIMITY);
+ when(mProxSensor.getMaximumRange()).thenReturn(5.0f);
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)).thenReturn(mGravitySensor);
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProxSensor);
+
+ mComponentContextFixture.putFloatResource(
+ R.dimen.device_on_ear_xy_gravity_threshold, 5.5f);
+ mComponentContextFixture.putFloatResource(
+ R.dimen.device_on_ear_y_gravity_negative_threshold, -1f);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @SmallTest
+ @Test
+ public void testListeners() throws Exception {
+ SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+
+ assertFalse(systemStateHelper.removeListener(mSystemStateListener));
+ systemStateHelper.addListener(mSystemStateListener);
+ assertTrue(systemStateHelper.removeListener(mSystemStateListener));
+ assertFalse(systemStateHelper.removeListener(mSystemStateListener));
+ }
+
+ @SmallTest
+ @Test
+ public void testQuerySystemForCarMode_True() {
+ when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager);
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ assertTrue(new SystemStateHelper(mContext).isCarMode());
+ }
+
+ @SmallTest
+ @Test
+ public void testQuerySystemForCarMode_False() {
+ when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager);
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_NORMAL);
+ assertFalse(new SystemStateHelper(mContext).isCarMode());
+ }
+
+ @SmallTest
+ @Test
+ public void testReceiverAndIntentFilter() {
+ ArgumentCaptor<IntentFilter> intentFilter = ArgumentCaptor.forClass(IntentFilter.class);
+ new SystemStateHelper(mContext);
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), intentFilter.capture());
+
+ assertEquals(2, intentFilter.getValue().countActions());
+ assertEquals(UiModeManager.ACTION_ENTER_CAR_MODE, intentFilter.getValue().getAction(0));
+ assertEquals(UiModeManager.ACTION_EXIT_CAR_MODE, intentFilter.getValue().getAction(1));
+ }
+
+ @SmallTest
+ @Test
+ public void testOnEnterExitCarMode() {
+ ArgumentCaptor<BroadcastReceiver> receiver =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ new SystemStateHelper(mContext).addListener(mSystemStateListener);
+
+ verify(mContext).registerReceiver(receiver.capture(), any(IntentFilter.class));
+
+ when(mIntentEnter.getAction()).thenReturn(UiModeManager.ACTION_ENTER_CAR_MODE);
+ receiver.getValue().onReceive(mContext, mIntentEnter);
+ verify(mSystemStateListener).onCarModeChanged(true);
+
+ when(mIntentExit.getAction()).thenReturn(UiModeManager.ACTION_EXIT_CAR_MODE);
+ receiver.getValue().onReceive(mContext, mIntentExit);
+ verify(mSystemStateListener).onCarModeChanged(false);
+
+ receiver.getValue().onReceive(mContext, new Intent("invalid action"));
+ }
+
+ @SmallTest
+ @Test
+ public void testDeviceOnEarCorrectlyDetected() {
+ doAnswer(invocation -> {
+ SensorEventListener listener = invocation.getArgument(0);
+ Sensor sensor = invocation.getArgument(1);
+ if (sensor.getType() == Sensor.TYPE_GRAVITY) {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, 9.0f, 1.0f}, Sensor.TYPE_GRAVITY));
+ } else {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{0.0f}, Sensor.TYPE_PROXIMITY));
+ }
+ return true;
+ }).when(mSensorManager)
+ .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt());
+
+ assertTrue(SystemStateHelper.isDeviceAtEar(mContext));
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ }
+
+ @SmallTest
+ @Test
+ public void testDeviceIsNotOnEarWithProxNotSensed() {
+ doAnswer(invocation -> {
+ SensorEventListener listener = invocation.getArgument(0);
+ Sensor sensor = invocation.getArgument(1);
+ if (sensor.getType() == Sensor.TYPE_GRAVITY) {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, 9.0f, 1.0f}, Sensor.TYPE_GRAVITY));
+ } else {
+ // do nothing to simulate proximity sensor not reporting
+ }
+ return true;
+ }).when(mSensorManager)
+ .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt());
+
+ assertFalse(SystemStateHelper.isDeviceAtEar(mContext));
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ }
+
+ @SmallTest
+ @Test
+ public void testDeviceIsNotOnEarWithWrongOrientation() {
+ doAnswer(invocation -> {
+ SensorEventListener listener = invocation.getArgument(0);
+ Sensor sensor = invocation.getArgument(1);
+ if (sensor.getType() == Sensor.TYPE_GRAVITY) {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, 1.0f, 9.0f}, Sensor.TYPE_GRAVITY));
+ } else {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{0.0f}, Sensor.TYPE_PROXIMITY));
+ }
+ return true;
+ }).when(mSensorManager)
+ .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt());
+
+ assertFalse(SystemStateHelper.isDeviceAtEar(mContext));
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ }
+
+ @SmallTest
+ @Test
+ public void testDeviceIsNotOnEarWithMissingSensor() {
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)).thenReturn(null);
+ doAnswer(invocation -> {
+ SensorEventListener listener = invocation.getArgument(0);
+ Sensor sensor = invocation.getArgument(1);
+ if (sensor.getType() == Sensor.TYPE_GRAVITY) {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, 9.0f, 1.0f}, Sensor.TYPE_GRAVITY));
+ } else {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{0.0f}, Sensor.TYPE_PROXIMITY));
+ }
+ return true;
+ }).when(mSensorManager)
+ .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt());
+
+ assertFalse(SystemStateHelper.isDeviceAtEar(mContext));
+ }
+
+ @SmallTest
+ @Test
+ public void testDeviceIsNotOnEarWithTimeout() {
+ doAnswer(invocation -> {
+ SensorEventListener listener = invocation.getArgument(0);
+ Sensor sensor = invocation.getArgument(1);
+ if (sensor.getType() == Sensor.TYPE_GRAVITY) {
+ // do nothing
+ } else {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{0.0f}, Sensor.TYPE_PROXIMITY));
+ }
+ return true;
+ }).when(mSensorManager)
+ .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt());
+
+ assertFalse(SystemStateHelper.isDeviceAtEar(mContext));
+ }
+
+ @SmallTest
+ @Test
+ public void testDeviceIsOnEarWithMultiSensorInputs() {
+ doAnswer(invocation -> {
+ SensorEventListener listener = invocation.getArgument(0);
+ Sensor sensor = invocation.getArgument(1);
+ if (sensor.getType() == Sensor.TYPE_GRAVITY) {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, 9.0f, 1.0f}, Sensor.TYPE_GRAVITY));
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, -9.0f, 1.0f}, Sensor.TYPE_GRAVITY));
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{1.0f, 0.0f, 8.0f}, Sensor.TYPE_GRAVITY));
+ } else {
+ listener.onSensorChanged(makeSensorEvent(
+ new float[]{0.0f}, Sensor.TYPE_PROXIMITY));
+ }
+ return true;
+ }).when(mSensorManager)
+ .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt());
+
+ assertTrue(SystemStateHelper.isDeviceAtEar(mContext));
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ }
+
+ private SensorEvent makeSensorEvent(float[] values, int sensorType) throws Exception {
+ SensorEvent event = mock(SensorEvent.class);
+ Sensor mockSensor = mock(Sensor.class);
+ when(mockSensor.getType()).thenReturn(sensorType);
+ FieldSetter.setField(event, SensorEvent.class.getDeclaredField("sensor"), mockSensor);
+ FieldSetter.setField(event, SensorEvent.class.getDeclaredField("values"), values);
+ return event;
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java b/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java
deleted file mode 100644
index 033f929..0000000
--- a/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java
+++ /dev/null
@@ -1,129 +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.server.telecom.tests;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.UiModeManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.telecom.SystemStateProvider;
-import com.android.server.telecom.SystemStateProvider.SystemStateListener;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Unit tests for SystemStateProvider
- */
-@RunWith(JUnit4.class)
-public class SystemStateProviderTest extends TelecomTestCase {
-
- @Mock Context mContext;
- @Mock SystemStateListener mSystemStateListener;
- @Mock UiModeManager mUiModeManager;
- @Mock Intent mIntentEnter;
- @Mock Intent mIntentExit;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- @SmallTest
- @Test
- public void testListeners() throws Exception {
- SystemStateProvider systemStateProvider = new SystemStateProvider(mContext);
-
- assertFalse(systemStateProvider.removeListener(mSystemStateListener));
- systemStateProvider.addListener(mSystemStateListener);
- assertTrue(systemStateProvider.removeListener(mSystemStateListener));
- assertFalse(systemStateProvider.removeListener(mSystemStateListener));
- }
-
- @SmallTest
- @Test
- public void testQuerySystemForCarMode_True() {
- when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager);
- when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
- assertTrue(new SystemStateProvider(mContext).isCarMode());
- }
-
- @SmallTest
- @Test
- public void testQuerySystemForCarMode_False() {
- when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager);
- when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_NORMAL);
- assertFalse(new SystemStateProvider(mContext).isCarMode());
- }
-
- @SmallTest
- @Test
- public void testReceiverAndIntentFilter() {
- ArgumentCaptor<IntentFilter> intentFilter = ArgumentCaptor.forClass(IntentFilter.class);
- new SystemStateProvider(mContext);
- verify(mContext).registerReceiver(any(BroadcastReceiver.class), intentFilter.capture());
-
- assertEquals(2, intentFilter.getValue().countActions());
- assertEquals(UiModeManager.ACTION_ENTER_CAR_MODE, intentFilter.getValue().getAction(0));
- assertEquals(UiModeManager.ACTION_EXIT_CAR_MODE, intentFilter.getValue().getAction(1));
- }
-
- @SmallTest
- @Test
- public void testOnEnterExitCarMode() {
- ArgumentCaptor<BroadcastReceiver> receiver =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- new SystemStateProvider(mContext).addListener(mSystemStateListener);
-
- verify(mContext).registerReceiver(receiver.capture(), any(IntentFilter.class));
-
- when(mIntentEnter.getAction()).thenReturn(UiModeManager.ACTION_ENTER_CAR_MODE);
- receiver.getValue().onReceive(mContext, mIntentEnter);
- verify(mSystemStateListener).onCarModeChanged(true);
-
- when(mIntentExit.getAction()).thenReturn(UiModeManager.ACTION_EXIT_CAR_MODE);
- receiver.getValue().onReceive(mContext, mIntentExit);
- verify(mSystemStateListener).onCarModeChanged(false);
-
- receiver.getValue().onReceive(mContext, new Intent("invalid action"));
- }
-}