CEC: Move finishWithCallback method to FeatureAction

Bug: 159901363
Test: None
Merged-In: Idd30ec162136c8b32e8cee1181c6e3e733aa9966
Change-Id: Idd30ec162136c8b32e8cee1181c6e3e733aa9966
This commit is contained in:
Aravind Angadi
2021-01-25 14:42:43 +05:30
committed by Marvin Ramin
parent a954e2dc75
commit b686aa5fd9
7 changed files with 76 additions and 138 deletions

View File

@@ -22,11 +22,8 @@ import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback; import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback;
import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult; import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import java.util.ArrayList;
import java.util.List;
/** /**
* Feature action that queries the power status of other device. This action is initiated via * Feature action that queries the power status of other device. This action is initiated via
@@ -42,7 +39,6 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
private static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1; private static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1;
private final int mTargetAddress; private final int mTargetAddress;
private final List<IHdmiControlCallback> mCallbacks = new ArrayList<>();
// Retry the power status query as it might happen when the target device is waking up. In // Retry the power status query as it might happen when the target device is waking up. In
// that case a device may be quite busy and can fail to respond within the 2s timeout. // that case a device may be quite busy and can fail to respond within the 2s timeout.
@@ -59,9 +55,8 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
private DevicePowerStatusAction(HdmiCecLocalDevice localDevice, private DevicePowerStatusAction(HdmiCecLocalDevice localDevice,
int targetAddress, IHdmiControlCallback callback) { int targetAddress, IHdmiControlCallback callback) {
super(localDevice); super(localDevice, callback);
mTargetAddress = targetAddress; mTargetAddress = targetAddress;
addCallback(callback);
} }
@Override @Override
@@ -74,8 +69,7 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
&& deviceInfo.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { && deviceInfo.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
int powerStatus = deviceInfo.getDevicePowerStatus(); int powerStatus = deviceInfo.getDevicePowerStatus();
if (powerStatus != HdmiControlManager.POWER_STATUS_UNKNOWN) { if (powerStatus != HdmiControlManager.POWER_STATUS_UNKNOWN) {
invokeCallback(powerStatus); finishWithCallback(powerStatus);
finish();
return true; return true;
} }
} }
@@ -93,8 +87,7 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
// the device is not present or not capable of CEC. // the device is not present or not capable of CEC.
if (error == SendMessageResult.NACK) { if (error == SendMessageResult.NACK) {
// Got no response from TV. Report status 'unknown'. // Got no response from TV. Report status 'unknown'.
invokeCallback(HdmiControlManager.POWER_STATUS_UNKNOWN); finishWithCallback(HdmiControlManager.POWER_STATUS_UNKNOWN);
finish();
} }
}); });
} }
@@ -107,8 +100,7 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
} }
if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
int status = cmd.getParams()[0]; int status = cmd.getParams()[0];
invokeCallback(status); finishWithCallback(status);
finish();
return true; return true;
} }
return false; return false;
@@ -127,22 +119,7 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
} }
// Got no response from TV. Report status 'unknown'. // Got no response from TV. Report status 'unknown'.
invokeCallback(HdmiControlManager.POWER_STATUS_UNKNOWN); finishWithCallback(HdmiControlManager.POWER_STATUS_UNKNOWN);
finish();
}
}
public void addCallback(IHdmiControlCallback callback) {
mCallbacks.add(callback);
}
private void invokeCallback(int result) {
try {
for (IHdmiControlCallback callback : mCallbacks) {
callback.onComplete(result);
}
} catch (RemoteException e) {
Slog.e(TAG, "Callback failed:" + e);
} }
} }
} }

View File

@@ -21,7 +21,6 @@ import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiTvClient; import android.hardware.hdmi.HdmiTvClient;
import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult; import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -63,7 +62,6 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3;
private final HdmiDeviceInfo mTarget; private final HdmiDeviceInfo mTarget;
private final IHdmiControlCallback mCallback;
private final HdmiCecMessage mGivePowerStatus; private final HdmiCecMessage mGivePowerStatus;
private final boolean mIsCec20; private final boolean mIsCec20;
@@ -86,8 +84,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
@VisibleForTesting @VisibleForTesting
DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target, DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target,
IHdmiControlCallback callback, boolean isCec20) { IHdmiControlCallback callback, boolean isCec20) {
super(source); super(source, callback);
mCallback = callback;
mTarget = target; mTarget = target;
mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
getSourceAddress(), getTargetAddress()); getSourceAddress(), getTargetAddress());
@@ -108,7 +105,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) { if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
queryDevicePowerStatus(); queryDevicePowerStatus();
} else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) { } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
invokeCallbackAndFinish(HdmiControlManager.RESULT_SUCCESS); finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
return true; return true;
} }
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
@@ -117,11 +114,14 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
} }
private void queryDevicePowerStatus() { private void queryDevicePowerStatus() {
sendCommand(mGivePowerStatus, new SendMessageCallback() { sendCommand(
mGivePowerStatus,
new SendMessageCallback() {
@Override @Override
public void onSendCompleted(int error) { public void onSendCompleted(int error) {
if (error != SendMessageResult.SUCCESS) { if (error != SendMessageResult.SUCCESS) {
invokeCallbackAndFinish(HdmiControlManager.RESULT_COMMUNICATION_FAILED); finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
return;
} }
} }
}); });
@@ -189,7 +189,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
switch (mState) { switch (mState) {
case STATE_WAIT_FOR_REPORT_POWER_STATUS: case STATE_WAIT_FOR_REPORT_POWER_STATUS:
if (tv().isPowerStandbyOrTransient()) { if (tv().isPowerStandbyOrTransient()) {
invokeCallbackAndFinish(HdmiControlManager.RESULT_INCORRECT_MODE); finishWithCallback(HdmiControlManager.RESULT_INCORRECT_MODE);
return; return;
} }
selectDevice(); selectDevice();
@@ -217,7 +217,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
if (!mIsCec20) { if (!mIsCec20) {
sendSetStreamPath(); sendSetStreamPath();
} }
invokeCallbackAndFinish(HdmiControlManager.RESULT_SUCCESS); finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
} }
private void sendSetStreamPath() { private void sendSetStreamPath() {
@@ -228,15 +228,4 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
getSourceAddress(), mTarget.getPhysicalAddress())); getSourceAddress(), mTarget.getPhysicalAddress()));
} }
private void invokeCallbackAndFinish(int result) {
if (mCallback != null) {
try {
mCallback.onComplete(result);
} catch (RemoteException e) {
Slog.e(TAG, "Callback failed:" + e);
}
}
finish();
}
} }

View File

@@ -15,9 +15,11 @@
*/ */
package com.android.server.hdmi; package com.android.server.hdmi;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
@@ -25,6 +27,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -61,7 +64,20 @@ abstract class HdmiCecFeatureAction {
private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks; private ArrayList<Pair<HdmiCecFeatureAction, Runnable>> mOnFinishedCallbacks;
final List<IHdmiControlCallback> mCallbacks = new ArrayList<>();
HdmiCecFeatureAction(HdmiCecLocalDevice source) { HdmiCecFeatureAction(HdmiCecLocalDevice source) {
this(source, new ArrayList<>());
}
HdmiCecFeatureAction(HdmiCecLocalDevice source, IHdmiControlCallback callback) {
this(source, Arrays.asList(callback));
}
HdmiCecFeatureAction(HdmiCecLocalDevice source, List<IHdmiControlCallback> callbacks) {
for (IHdmiControlCallback callback : callbacks) {
addCallback(callback);
}
mSource = source; mSource = source;
mService = mSource.getService(); mService = mSource.getService();
mActionTimer = createActionTimer(mService.getServiceLooper()); mActionTimer = createActionTimer(mService.getServiceLooper());
@@ -282,4 +298,26 @@ abstract class HdmiCecFeatureAction {
} }
mOnFinishedCallbacks.add(Pair.create(action, runnable)); mOnFinishedCallbacks.add(Pair.create(action, runnable));
} }
protected void finishWithCallback(int returnCode) {
invokeCallback(returnCode);
finish();
}
public void addCallback(IHdmiControlCallback callback) {
mCallbacks.add(callback);
}
private void invokeCallback(int result) {
try {
for (IHdmiControlCallback callback : mCallbacks) {
if (callback == null) {
continue;
}
callback.onComplete(result);
}
} catch (RemoteException e) {
Slog.e(TAG, "Callback failed:" + e);
}
}
} }

View File

@@ -18,11 +18,8 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import java.util.ArrayList;
import java.util.List;
/** /**
* Feature action that performs one touch play against TV/Display device. This action is initiated * Feature action that performs one touch play against TV/Display device. This action is initiated
@@ -50,7 +47,6 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
private static final int LOOP_COUNTER_MAX = 10; private static final int LOOP_COUNTER_MAX = 10;
private final int mTargetAddress; private final int mTargetAddress;
private final List<IHdmiControlCallback> mCallbacks = new ArrayList<>();
private int mPowerStatusCounter = 0; private int mPowerStatusCounter = 0;
@@ -69,9 +65,8 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress, private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
IHdmiControlCallback callback) { IHdmiControlCallback callback) {
super(localDevice); super(localDevice, callback);
mTargetAddress = targetAddress; mTargetAddress = targetAddress;
addCallback(callback);
} }
@Override @Override
@@ -121,8 +116,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
int status = cmd.getParams()[0]; int status = cmd.getParams()[0];
if (status == HdmiControlManager.POWER_STATUS_ON) { if (status == HdmiControlManager.POWER_STATUS_ON) {
broadcastActiveSource(); broadcastActiveSource();
invokeCallback(HdmiControlManager.RESULT_SUCCESS); finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
finish();
} }
return true; return true;
} }
@@ -140,26 +134,11 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
addTimer(mState, HdmiConfig.TIMEOUT_MS); addTimer(mState, HdmiConfig.TIMEOUT_MS);
} else { } else {
// Couldn't wake up the TV for whatever reason. Report failure. // Couldn't wake up the TV for whatever reason. Report failure.
invokeCallback(HdmiControlManager.RESULT_TIMEOUT); finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
finish();
} }
} }
} }
public void addCallback(IHdmiControlCallback callback) {
mCallbacks.add(callback);
}
private void invokeCallback(int result) {
try {
for (IHdmiControlCallback callback : mCallbacks) {
callback.onComplete(result);
}
} catch (RemoteException e) {
Slog.e(TAG, "Callback failed:" + e);
}
}
private boolean shouldTurnOnConnectedAudioSystem() { private boolean shouldTurnOnConnectedAudioSystem() {
HdmiControlService service = mSource.mService; HdmiControlService service = mSource.mService;
if (service.isAudioSystemDevice()) { if (service.isAudioSystemDevice()) {
@@ -170,4 +149,5 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
} }
} }

View File

@@ -16,11 +16,9 @@
package com.android.server.hdmi; package com.android.server.hdmi;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
@@ -66,15 +64,12 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
// <Inactive Source> command. // <Inactive Source> command.
private final boolean mNotifyInputChange; private final boolean mNotifyInputChange;
@Nullable private final IHdmiControlCallback mCallback;
// The latest routing path. Updated by each <Routing Information> from CEC switches. // The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath; private int mCurrentRoutingPath;
RoutingControlAction(HdmiCecLocalDevice localDevice, int path, boolean queryDevicePowerStatus, RoutingControlAction(HdmiCecLocalDevice localDevice, int path, boolean queryDevicePowerStatus,
IHdmiControlCallback callback) { IHdmiControlCallback callback) {
super(localDevice); super(localDevice, callback);
mCallback = callback;
mCurrentRoutingPath = path; mCurrentRoutingPath = path;
mQueryDevicePowerStatus = queryDevicePowerStatus; mQueryDevicePowerStatus = queryDevicePowerStatus;
// Callback is non-null when routing control action is brought up by binder API. Use // Callback is non-null when routing control action is brought up by binder API. Use
@@ -147,11 +142,6 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
mCurrentRoutingPath)); mCurrentRoutingPath));
} }
private void finishWithCallback(int result) {
invokeCallback(result);
finish();
}
@Override @Override
public void handleTimerEvent(int timeoutState) { public void handleTimerEvent(int timeoutState) {
if (mState != timeoutState || mState == STATE_NONE) { if (mState != timeoutState || mState == STATE_NONE) {
@@ -202,15 +192,4 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
finishWithCallback(HdmiControlManager.RESULT_SUCCESS); finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
} }
} }
private void invokeCallback(int result) {
if (mCallback == null) {
return;
}
try {
mCallback.onComplete(result);
} catch (RemoteException e) {
// Do nothing.
}
}
} }

View File

@@ -16,13 +16,11 @@
package com.android.server.hdmi; package com.android.server.hdmi;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult; import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.RemoteException;
import android.util.Slog;
import java.util.List; import java.util.List;
/** /**
@@ -49,8 +47,6 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
// The target audio status of the action, whether to enable the system audio mode or not. // The target audio status of the action, whether to enable the system audio mode or not.
protected boolean mTargetAudioStatus; protected boolean mTargetAudioStatus;
@Nullable private final IHdmiControlCallback mCallback;
private int mSendRetryCount = 0; private int mSendRetryCount = 0;
/** /**
@@ -64,11 +60,10 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
*/ */
SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus, SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
IHdmiControlCallback callback) { IHdmiControlCallback callback) {
super(source); super(source, callback);
HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
mAvrLogicalAddress = avrAddress; mAvrLogicalAddress = avrAddress;
mTargetAudioStatus = targetStatus; mTargetAudioStatus = targetStatus;
mCallback = callback;
} }
// Seq #27 // Seq #27
@@ -174,7 +169,7 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
} }
protected void startAudioStatusAction() { protected void startAudioStatusAction() {
addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback)); addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallbacks));
finish(); finish();
} }
@@ -194,17 +189,4 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
return; return;
} }
} }
// TODO: if IHdmiControlCallback is general to other FeatureAction,
// move it into FeatureAction.
protected void finishWithCallback(int returnCode) {
if (mCallback != null) {
try {
mCallback.onComplete(returnCode);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to invoke callback.", e);
}
}
finish();
}
} }

View File

@@ -16,14 +16,14 @@
package com.android.server.hdmi; package com.android.server.hdmi;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult; import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import java.util.List;
/** /**
* Action to update audio status (volume or mute) of audio amplifier * Action to update audio status (volume or mute) of audio amplifier
*/ */
@@ -34,13 +34,17 @@ final class SystemAudioStatusAction extends HdmiCecFeatureAction {
private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1; private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
private final int mAvrAddress; private final int mAvrAddress;
@Nullable private final IHdmiControlCallback mCallback;
SystemAudioStatusAction(
HdmiCecLocalDevice source, int avrAddress, List<IHdmiControlCallback> callbacks) {
super(source, callbacks);
mAvrAddress = avrAddress;
}
SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress, SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress,
IHdmiControlCallback callback) { IHdmiControlCallback callback) {
super(source); super(source, callback);
mAvrAddress = avrAddress; mAvrAddress = avrAddress;
mCallback = callback;
} }
@Override @Override
@@ -97,17 +101,6 @@ final class SystemAudioStatusAction extends HdmiCecFeatureAction {
finishWithCallback(HdmiControlManager.RESULT_SUCCESS); finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
} }
private void finishWithCallback(int returnCode) {
if (mCallback != null) {
try {
mCallback.onComplete(returnCode);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to invoke callback.", e);
}
}
finish();
}
@Override @Override
void handleTimerEvent(int state) { void handleTimerEvent(int state) {
if (mState != state) { if (mState != state) {