1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.telecom;
18 
19 import android.media.AudioManager;
20 import android.os.Message;
21 import android.telecom.Log;
22 import android.telecom.Logging.Runnable;
23 import android.telecom.Logging.Session;
24 import android.util.SparseArray;
25 
26 import com.android.internal.util.IState;
27 import com.android.internal.util.IndentingPrintWriter;
28 import com.android.internal.util.State;
29 import com.android.internal.util.StateMachine;
30 
31 public class CallAudioModeStateMachine extends StateMachine {
32     public static class MessageArgs {
33         public boolean hasActiveOrDialingCalls;
34         public boolean hasRingingCalls;
35         public boolean hasHoldingCalls;
36         public boolean isTonePlaying;
37         public boolean foregroundCallIsVoip;
38         public Session session;
39 
MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls, boolean hasHoldingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip, Session session)40         public MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
41                 boolean hasHoldingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip,
42                 Session session) {
43             this.hasActiveOrDialingCalls = hasActiveOrDialingCalls;
44             this.hasRingingCalls = hasRingingCalls;
45             this.hasHoldingCalls = hasHoldingCalls;
46             this.isTonePlaying = isTonePlaying;
47             this.foregroundCallIsVoip = foregroundCallIsVoip;
48             this.session = session;
49         }
50 
MessageArgs()51         public MessageArgs() {
52             this.session = Log.createSubsession();
53         }
54 
55         @Override
toString()56         public String toString() {
57             return "MessageArgs{" +
58                     "hasActiveCalls=" + hasActiveOrDialingCalls +
59                     ", hasRingingCalls=" + hasRingingCalls +
60                     ", hasHoldingCalls=" + hasHoldingCalls +
61                     ", isTonePlaying=" + isTonePlaying +
62                     ", foregroundCallIsVoip=" + foregroundCallIsVoip +
63                     ", session=" + session +
64                     '}';
65         }
66     }
67 
68     public static final int INITIALIZE = 1;
69     // These ENTER_*_FOCUS commands are for testing.
70     public static final int ENTER_CALL_FOCUS_FOR_TESTING = 2;
71     public static final int ENTER_COMMS_FOCUS_FOR_TESTING = 3;
72     public static final int ENTER_RING_FOCUS_FOR_TESTING = 4;
73     public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5;
74     public static final int ABANDON_FOCUS_FOR_TESTING = 6;
75 
76     public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001;
77     public static final int NO_MORE_RINGING_CALLS = 1002;
78     public static final int NO_MORE_HOLDING_CALLS = 1003;
79 
80     public static final int NEW_ACTIVE_OR_DIALING_CALL = 2001;
81     public static final int NEW_RINGING_CALL = 2002;
82     public static final int NEW_HOLDING_CALL = 2003;
83     public static final int MT_AUDIO_SPEEDUP_FOR_RINGING_CALL = 2004;
84 
85     public static final int TONE_STARTED_PLAYING = 3001;
86     public static final int TONE_STOPPED_PLAYING = 3002;
87 
88     public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001;
89 
90     public static final int RUN_RUNNABLE = 9001;
91 
92     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
93         put(ENTER_CALL_FOCUS_FOR_TESTING, "ENTER_CALL_FOCUS_FOR_TESTING");
94         put(ENTER_COMMS_FOCUS_FOR_TESTING, "ENTER_COMMS_FOCUS_FOR_TESTING");
95         put(ENTER_RING_FOCUS_FOR_TESTING, "ENTER_RING_FOCUS_FOR_TESTING");
96         put(ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, "ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING");
97         put(ABANDON_FOCUS_FOR_TESTING, "ABANDON_FOCUS_FOR_TESTING");
98         put(NO_MORE_ACTIVE_OR_DIALING_CALLS, "NO_MORE_ACTIVE_OR_DIALING_CALLS");
99         put(NO_MORE_RINGING_CALLS, "NO_MORE_RINGING_CALLS");
100         put(NO_MORE_HOLDING_CALLS, "NO_MORE_HOLDING_CALLS");
101         put(NEW_ACTIVE_OR_DIALING_CALL, "NEW_ACTIVE_OR_DIALING_CALL");
102         put(NEW_RINGING_CALL, "NEW_RINGING_CALL");
103         put(NEW_HOLDING_CALL, "NEW_HOLDING_CALL");
104         put(MT_AUDIO_SPEEDUP_FOR_RINGING_CALL, "MT_AUDIO_SPEEDUP_FOR_RINGING_CALL");
105         put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING");
106         put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING");
107         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
108 
109         put(RUN_RUNNABLE, "RUN_RUNNABLE");
110     }};
111 
112     public static final String TONE_HOLD_STATE_NAME = OtherFocusState.class.getSimpleName();
113     public static final String UNFOCUSED_STATE_NAME = UnfocusedState.class.getSimpleName();
114     public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName();
115     public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName();
116     public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName();
117 
118     private class BaseState extends State {
119         @Override
processMessage(Message msg)120         public boolean processMessage(Message msg) {
121             switch (msg.what) {
122                 case ENTER_CALL_FOCUS_FOR_TESTING:
123                     transitionTo(mSimCallFocusState);
124                     return HANDLED;
125                 case ENTER_COMMS_FOCUS_FOR_TESTING:
126                     transitionTo(mVoipCallFocusState);
127                     return HANDLED;
128                 case ENTER_RING_FOCUS_FOR_TESTING:
129                     transitionTo(mRingingFocusState);
130                     return HANDLED;
131                 case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING:
132                     transitionTo(mOtherFocusState);
133                     return HANDLED;
134                 case ABANDON_FOCUS_FOR_TESTING:
135                     transitionTo(mUnfocusedState);
136                     return HANDLED;
137                 case INITIALIZE:
138                     mIsInitialized = true;
139                     return HANDLED;
140                 case RUN_RUNNABLE:
141                     java.lang.Runnable r = (java.lang.Runnable) msg.obj;
142                     r.run();
143                     return HANDLED;
144                 default:
145                     return NOT_HANDLED;
146             }
147         }
148     }
149 
150     private class UnfocusedState extends BaseState {
151         @Override
enter()152         public void enter() {
153             if (mIsInitialized) {
154                 Log.i(LOG_TAG, "Abandoning audio focus: now UNFOCUSED");
155                 mAudioManager.abandonAudioFocusForCall();
156                 mAudioManager.setMode(AudioManager.MODE_NORMAL);
157 
158                 mMostRecentMode = AudioManager.MODE_NORMAL;
159                 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
160             }
161         }
162 
163         @Override
processMessage(Message msg)164         public boolean processMessage(Message msg) {
165             if (super.processMessage(msg) == HANDLED) {
166                 return HANDLED;
167             }
168             MessageArgs args = (MessageArgs) msg.obj;
169             switch (msg.what) {
170                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
171                     // Do nothing.
172                     return HANDLED;
173                 case NO_MORE_RINGING_CALLS:
174                     // Do nothing.
175                     return HANDLED;
176                 case NO_MORE_HOLDING_CALLS:
177                     // Do nothing.
178                     return HANDLED;
179                 case NEW_ACTIVE_OR_DIALING_CALL:
180                     transitionTo(args.foregroundCallIsVoip
181                             ? mVoipCallFocusState : mSimCallFocusState);
182                     return HANDLED;
183                 case NEW_RINGING_CALL:
184                     transitionTo(mRingingFocusState);
185                     return HANDLED;
186                 case NEW_HOLDING_CALL:
187                     // This really shouldn't happen, but transition to the focused state anyway.
188                     Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
189                             " Args are: \n" + args.toString());
190                     transitionTo(mOtherFocusState);
191                     return HANDLED;
192                 case TONE_STARTED_PLAYING:
193                     // This shouldn't happen either, but perform the action anyway.
194                     Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
195                             + args.toString());
196                     return HANDLED;
197                 default:
198                     // The forced focus switch commands are handled by BaseState.
199                     return NOT_HANDLED;
200             }
201         }
202     }
203 
204     private class RingingFocusState extends BaseState {
205         @Override
enter()206         public void enter() {
207             Log.i(LOG_TAG, "Audio focus entering RINGING state");
208             if (mCallAudioManager.startRinging()) {
209                 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
210                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
211                 if (mMostRecentMode == AudioManager.MODE_IN_CALL) {
212                     // Preserving behavior from the old CallAudioManager.
213                     Log.i(LOG_TAG, "Transition from IN_CALL -> RINGTONE."
214                             + "  Resetting to NORMAL first.");
215                     mAudioManager.setMode(AudioManager.MODE_NORMAL);
216                 }
217                 mAudioManager.setMode(AudioManager.MODE_RINGTONE);
218                 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.RINGING_FOCUS);
219             } else {
220                 Log.i(LOG_TAG, "Entering RINGING but not acquiring focus -- silent ringtone");
221             }
222 
223             mCallAudioManager.stopCallWaiting();
224         }
225 
226         @Override
exit()227         public void exit() {
228             // Audio mode and audio stream will be set by the next state.
229             mCallAudioManager.stopRinging();
230         }
231 
232         @Override
processMessage(Message msg)233         public boolean processMessage(Message msg) {
234             if (super.processMessage(msg) == HANDLED) {
235                 return HANDLED;
236             }
237             MessageArgs args = (MessageArgs) msg.obj;
238             switch (msg.what) {
239                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
240                     // Do nothing. Loss of an active call should not impact ringer.
241                     return HANDLED;
242                 case NO_MORE_HOLDING_CALLS:
243                     // Do nothing and keep ringing.
244                     return HANDLED;
245                 case NO_MORE_RINGING_CALLS:
246                     // If there are active or holding calls, switch to the appropriate focus.
247                     // Otherwise abandon focus.
248                     if (args.hasActiveOrDialingCalls) {
249                         if (args.foregroundCallIsVoip) {
250                             transitionTo(mVoipCallFocusState);
251                         } else {
252                             transitionTo(mSimCallFocusState);
253                         }
254                     } else if (args.hasHoldingCalls || args.isTonePlaying) {
255                         transitionTo(mOtherFocusState);
256                     } else {
257                         transitionTo(mUnfocusedState);
258                     }
259                     return HANDLED;
260                 case NEW_ACTIVE_OR_DIALING_CALL:
261                     // If a call becomes active suddenly, give it priority over ringing.
262                     transitionTo(args.foregroundCallIsVoip
263                             ? mVoipCallFocusState : mSimCallFocusState);
264                     return HANDLED;
265                 case NEW_RINGING_CALL:
266                     Log.w(LOG_TAG, "Unexpected behavior! New ringing call appeared while in " +
267                             "ringing state.");
268                     return HANDLED;
269                 case NEW_HOLDING_CALL:
270                     // This really shouldn't happen, but transition to the focused state anyway.
271                     Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." +
272                             " Args are: " + args.toString());
273                     transitionTo(mOtherFocusState);
274                     return HANDLED;
275                 case MT_AUDIO_SPEEDUP_FOR_RINGING_CALL:
276                     // This happens when an IMS call is answered by the in-call UI. Special case
277                     // that we have to deal with for some reason.
278 
279                     // VOIP calls should never invoke this mechanism, so transition directly to
280                     // the sim call focus state.
281                     transitionTo(mSimCallFocusState);
282                     return HANDLED;
283                 default:
284                     // The forced focus switch commands are handled by BaseState.
285                     return NOT_HANDLED;
286             }
287         }
288     }
289 
290     private class SimCallFocusState extends BaseState {
291         @Override
enter()292         public void enter() {
293             Log.i(LOG_TAG, "Audio focus entering SIM CALL state");
294             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
295                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
296             mAudioManager.setMode(AudioManager.MODE_IN_CALL);
297             mMostRecentMode = AudioManager.MODE_IN_CALL;
298             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
299         }
300 
301         @Override
processMessage(Message msg)302         public boolean processMessage(Message msg) {
303             if (super.processMessage(msg) == HANDLED) {
304                 return HANDLED;
305             }
306             MessageArgs args = (MessageArgs) msg.obj;
307             switch (msg.what) {
308                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
309                     // Switch to either ringing, holding, or inactive
310                     transitionTo(destinationStateAfterNoMoreActiveCalls(args));
311                     return HANDLED;
312                 case NO_MORE_RINGING_CALLS:
313                     // Don't transition state, but stop any call-waiting tones that may have been
314                     // playing.
315                     if (args.isTonePlaying) {
316                         mCallAudioManager.stopCallWaiting();
317                     }
318                     // If a MT-audio-speedup call gets disconnected by the connection service
319                     // concurrently with the user answering it, we may get this message
320                     // indicating that a ringing call has disconnected while this state machine
321                     // is in the SimCallFocusState.
322                     if (!args.hasActiveOrDialingCalls) {
323                         transitionTo(destinationStateAfterNoMoreActiveCalls(args));
324                     }
325                     return HANDLED;
326                 case NO_MORE_HOLDING_CALLS:
327                     // Do nothing.
328                     return HANDLED;
329                 case NEW_ACTIVE_OR_DIALING_CALL:
330                     // Do nothing. Already active.
331                     return HANDLED;
332                 case NEW_RINGING_CALL:
333                     // Don't make a call ring over an active call, but do play a call waiting tone.
334                     mCallAudioManager.startCallWaiting();
335                     return HANDLED;
336                 case NEW_HOLDING_CALL:
337                     // Don't do anything now. Putting an active call on hold will be handled when
338                     // NO_MORE_ACTIVE_CALLS is processed.
339                     return HANDLED;
340                 case FOREGROUND_VOIP_MODE_CHANGE:
341                     if (args.foregroundCallIsVoip) {
342                         transitionTo(mVoipCallFocusState);
343                     }
344                     return HANDLED;
345                 default:
346                     // The forced focus switch commands are handled by BaseState.
347                     return NOT_HANDLED;
348             }
349         }
350     }
351 
352     private class VoipCallFocusState extends BaseState {
353         @Override
enter()354         public void enter() {
355             Log.i(LOG_TAG, "Audio focus entering VOIP CALL state");
356             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
357                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
358             mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
359             mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION;
360             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
361         }
362 
363         @Override
processMessage(Message msg)364         public boolean processMessage(Message msg) {
365             if (super.processMessage(msg) == HANDLED) {
366                 return HANDLED;
367             }
368             MessageArgs args = (MessageArgs) msg.obj;
369             switch (msg.what) {
370                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
371                     // Switch to either ringing, holding, or inactive
372                     transitionTo(destinationStateAfterNoMoreActiveCalls(args));
373                     return HANDLED;
374                 case NO_MORE_RINGING_CALLS:
375                     // Don't transition state, but stop any call-waiting tones that may have been
376                     // playing.
377                     if (args.isTonePlaying) {
378                         mCallAudioManager.stopCallWaiting();
379                     }
380                     return HANDLED;
381                 case NO_MORE_HOLDING_CALLS:
382                     // Do nothing.
383                     return HANDLED;
384                 case NEW_ACTIVE_OR_DIALING_CALL:
385                     // Do nothing. Already active.
386                     return HANDLED;
387                 case NEW_RINGING_CALL:
388                     // Don't make a call ring over an active call, but do play a call waiting tone.
389                     mCallAudioManager.startCallWaiting();
390                     return HANDLED;
391                 case NEW_HOLDING_CALL:
392                     // Don't do anything now. Putting an active call on hold will be handled when
393                     // NO_MORE_ACTIVE_CALLS is processed.
394                     return HANDLED;
395                 case FOREGROUND_VOIP_MODE_CHANGE:
396                     if (!args.foregroundCallIsVoip) {
397                         transitionTo(mSimCallFocusState);
398                     }
399                     return HANDLED;
400                 default:
401                     // The forced focus switch commands are handled by BaseState.
402                     return NOT_HANDLED;
403             }
404         }
405     }
406 
407     /**
408      * This class is used for calls on hold and end-of-call tones.
409      */
410     private class OtherFocusState extends BaseState {
411         @Override
enter()412         public void enter() {
413             Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state");
414             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
415                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
416             mAudioManager.setMode(mMostRecentMode);
417             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
418         }
419 
420         @Override
processMessage(Message msg)421         public boolean processMessage(Message msg) {
422             if (super.processMessage(msg) == HANDLED) {
423                 return HANDLED;
424             }
425             MessageArgs args = (MessageArgs) msg.obj;
426             switch (msg.what) {
427                 case NO_MORE_HOLDING_CALLS:
428                     if (args.hasActiveOrDialingCalls) {
429                         transitionTo(args.foregroundCallIsVoip
430                                 ? mVoipCallFocusState : mSimCallFocusState);
431                     } else if (args.hasRingingCalls) {
432                         transitionTo(mRingingFocusState);
433                     } else if (!args.isTonePlaying) {
434                         transitionTo(mUnfocusedState);
435                     }
436                     // Do nothing if a tone is playing.
437                     return HANDLED;
438                 case NEW_ACTIVE_OR_DIALING_CALL:
439                     transitionTo(args.foregroundCallIsVoip
440                             ? mVoipCallFocusState : mSimCallFocusState);
441                     return HANDLED;
442                 case NEW_RINGING_CALL:
443                     // Apparently this is current behavior. Should this be the case?
444                     transitionTo(mRingingFocusState);
445                     return HANDLED;
446                 case NEW_HOLDING_CALL:
447                     // Do nothing.
448                     return HANDLED;
449                 case NO_MORE_RINGING_CALLS:
450                     // If there are no more ringing calls in this state, then stop any call-waiting
451                     // tones that may be playing.
452                     mCallAudioManager.stopCallWaiting();
453                     return HANDLED;
454                 case TONE_STOPPED_PLAYING:
455                     transitionTo(destinationStateAfterNoMoreActiveCalls(args));
456                 default:
457                     return NOT_HANDLED;
458             }
459         }
460     }
461 
462     private static final String LOG_TAG = CallAudioModeStateMachine.class.getSimpleName();
463 
464     private final BaseState mUnfocusedState = new UnfocusedState();
465     private final BaseState mRingingFocusState = new RingingFocusState();
466     private final BaseState mSimCallFocusState = new SimCallFocusState();
467     private final BaseState mVoipCallFocusState = new VoipCallFocusState();
468     private final BaseState mOtherFocusState = new OtherFocusState();
469 
470     private final AudioManager mAudioManager;
471     private CallAudioManager mCallAudioManager;
472 
473     private int mMostRecentMode;
474     private boolean mIsInitialized = false;
475 
CallAudioModeStateMachine(AudioManager audioManager)476     public CallAudioModeStateMachine(AudioManager audioManager) {
477         super(CallAudioModeStateMachine.class.getSimpleName());
478         mAudioManager = audioManager;
479         mMostRecentMode = AudioManager.MODE_NORMAL;
480 
481         addState(mUnfocusedState);
482         addState(mRingingFocusState);
483         addState(mSimCallFocusState);
484         addState(mVoipCallFocusState);
485         addState(mOtherFocusState);
486         setInitialState(mUnfocusedState);
487         start();
488         sendMessage(INITIALIZE, new MessageArgs());
489     }
490 
setCallAudioManager(CallAudioManager callAudioManager)491     public void setCallAudioManager(CallAudioManager callAudioManager) {
492         mCallAudioManager = callAudioManager;
493     }
494 
getCurrentStateName()495     public String getCurrentStateName() {
496         IState currentState = getCurrentState();
497         return currentState == null ? "no state" : currentState.getName();
498     }
499 
sendMessageWithArgs(int messageCode, MessageArgs args)500     public void sendMessageWithArgs(int messageCode, MessageArgs args) {
501         sendMessage(messageCode, args);
502     }
503 
504     @Override
onPreHandleMessage(Message msg)505     protected void onPreHandleMessage(Message msg) {
506         if (msg.obj != null && msg.obj instanceof MessageArgs) {
507             Log.continueSession(((MessageArgs) msg.obj).session, "CAMSM.pM_" + msg.what);
508             Log.i(LOG_TAG, "Message received: %s.", MESSAGE_CODE_TO_NAME.get(msg.what));
509         } else if (msg.what == RUN_RUNNABLE && msg.obj instanceof Runnable) {
510             Log.i(LOG_TAG, "Running runnable for testing");
511         } else {
512                 Log.w(LOG_TAG, "Message sent must be of type nonnull MessageArgs, but got " +
513                         (msg.obj == null ? "null" : msg.obj.getClass().getSimpleName()));
514                 Log.w(LOG_TAG, "The message was of code %d = %s",
515                         msg.what, MESSAGE_CODE_TO_NAME.get(msg.what));
516         }
517     }
518 
dumpPendingMessages(IndentingPrintWriter pw)519     public void dumpPendingMessages(IndentingPrintWriter pw) {
520         getHandler().getLooper().dump(pw::println, "");
521     }
522 
523     @Override
onPostHandleMessage(Message msg)524     protected void onPostHandleMessage(Message msg) {
525         Log.endSession();
526     }
527 
destinationStateAfterNoMoreActiveCalls(MessageArgs args)528     private BaseState destinationStateAfterNoMoreActiveCalls(MessageArgs args) {
529         if (args.hasHoldingCalls) {
530             return mOtherFocusState;
531         } else if (args.hasRingingCalls) {
532             return mRingingFocusState;
533         } else if (args.isTonePlaying) {
534             return mOtherFocusState;
535         } else {
536             return mUnfocusedState;
537         }
538     }
539 }
540