GsmConnection.java revision 1220a4e283def0598468376cf112d3b904026fb8
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony.gsm;
18import android.content.Context;
19import android.os.AsyncResult;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.Message;
23import android.os.PowerManager;
24import android.os.Registrant;
25import android.os.SystemClock;
26import android.telephony.Rlog;
27import android.telephony.PhoneNumberUtils;
28import android.telephony.ServiceState;
29import android.text.TextUtils;
30
31import com.android.internal.telephony.*;
32import com.android.internal.telephony.uicc.UiccCardApplication;
33import com.android.internal.telephony.uicc.UiccController;
34import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
35
36/**
37 * {@hide}
38 */
39public class GsmConnection extends Connection {
40    private static final String LOG_TAG = "GsmConnection";
41    private static final boolean DBG = true;
42
43    //***** Instance Variables
44
45    GsmCallTracker mOwner;
46    GsmCall mParent;
47
48    String mAddress;     // MAY BE NULL!!!
49    String mDialString;          // outgoing calls only
50    String mPostDialString;      // outgoing calls only
51    boolean mIsIncoming;
52    boolean mDisconnected;
53
54    int mIndex;          // index in GsmCallTracker.connections[], -1 if unassigned
55                        // The GSM index is 1 + this
56
57    /*
58     * These time/timespan values are based on System.currentTimeMillis(),
59     * i.e., "wall clock" time.
60     */
61    long mCreateTime;
62    long mConnectTime;
63    long mDisconnectTime;
64
65    /*
66     * These time/timespan values are based on SystemClock.elapsedRealTime(),
67     * i.e., time since boot.  They are appropriate for comparison and
68     * calculating deltas.
69     */
70    long mConnectTimeReal;
71    long mDuration;
72    long mHoldingStartTime;  // The time when the Connection last transitioned
73                            // into HOLDING
74
75    int mNextPostDialChar;       // index into postDialString
76
77    DisconnectCause mCause = DisconnectCause.NOT_DISCONNECTED;
78    PostDialState mPostDialState = PostDialState.NOT_STARTED;
79    int mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
80    UUSInfo mUusInfo;
81
82    Handler mHandler;
83
84    private PowerManager.WakeLock mPartialWakeLock;
85
86    //***** Event Constants
87    static final int EVENT_DTMF_DONE = 1;
88    static final int EVENT_PAUSE_DONE = 2;
89    static final int EVENT_NEXT_POST_DIAL = 3;
90    static final int EVENT_WAKE_LOCK_TIMEOUT = 4;
91
92    //***** Constants
93    static final int PAUSE_DELAY_MILLIS = 3 * 1000;
94    static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000;
95
96    //***** Inner Classes
97
98    class MyHandler extends Handler {
99        MyHandler(Looper l) {super(l);}
100
101        @Override
102        public void
103        handleMessage(Message msg) {
104
105            switch (msg.what) {
106                case EVENT_NEXT_POST_DIAL:
107                case EVENT_DTMF_DONE:
108                case EVENT_PAUSE_DONE:
109                    processNextPostDialChar();
110                    break;
111                case EVENT_WAKE_LOCK_TIMEOUT:
112                    releaseWakeLock();
113                    break;
114            }
115        }
116    }
117
118    //***** Constructors
119
120    /** This is probably an MT call that we first saw in a CLCC response */
121    /*package*/
122    GsmConnection (Context context, DriverCall dc, GsmCallTracker ct, int index) {
123        createWakeLock(context);
124        acquireWakeLock();
125
126        mOwner = ct;
127        mHandler = new MyHandler(mOwner.getLooper());
128
129        mAddress = dc.number;
130
131        mIsIncoming = dc.isMT;
132        mCreateTime = System.currentTimeMillis();
133        mCnapName = dc.name;
134        mCnapNamePresentation = dc.namePresentation;
135        mNumberPresentation = dc.numberPresentation;
136        mUusInfo = dc.uusInfo;
137
138        mIndex = index;
139
140        mParent = parentFromDCState (dc.state);
141        mParent.attach(this, dc);
142    }
143
144    /** This is an MO call, created when dialing */
145    /*package*/
146    GsmConnection (Context context, String dialString, GsmCallTracker ct, GsmCall parent) {
147        createWakeLock(context);
148        acquireWakeLock();
149
150        mOwner = ct;
151        mHandler = new MyHandler(mOwner.getLooper());
152
153        mDialString = dialString;
154
155        mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
156        mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
157
158        mIndex = -1;
159
160        mIsIncoming = false;
161        mCnapName = null;
162        mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
163        mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
164        mCreateTime = System.currentTimeMillis();
165
166        mParent = parent;
167        parent.attachFake(this, GsmCall.State.DIALING);
168    }
169
170    public void dispose() {
171    }
172
173    static boolean
174    equalsHandlesNulls (Object a, Object b) {
175        return (a == null) ? (b == null) : a.equals (b);
176    }
177
178    /*package*/ boolean
179    compareTo(DriverCall c) {
180        // On mobile originated (MO) calls, the phone number may have changed
181        // due to a SIM Toolkit call control modification.
182        //
183        // We assume we know when MO calls are created (since we created them)
184        // and therefore don't need to compare the phone number anyway.
185        if (! (mIsIncoming || c.isMT)) return true;
186
187        // ... but we can compare phone numbers on MT calls, and we have
188        // no control over when they begin, so we might as well
189
190        String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA);
191        return mIsIncoming == c.isMT && equalsHandlesNulls(mAddress, cAddress);
192    }
193
194    @Override
195    public String getAddress() {
196        return mAddress;
197    }
198
199    @Override
200    public GsmCall getCall() {
201        return mParent;
202    }
203
204    @Override
205    public long getCreateTime() {
206        return mCreateTime;
207    }
208
209    @Override
210    public long getConnectTime() {
211        return mConnectTime;
212    }
213
214    @Override
215    public long getDisconnectTime() {
216        return mDisconnectTime;
217    }
218
219    @Override
220    public long getDurationMillis() {
221        if (mConnectTimeReal == 0) {
222            return 0;
223        } else if (mDuration == 0) {
224            return SystemClock.elapsedRealtime() - mConnectTimeReal;
225        } else {
226            return mDuration;
227        }
228    }
229
230    @Override
231    public long getHoldDurationMillis() {
232        if (getState() != GsmCall.State.HOLDING) {
233            // If not holding, return 0
234            return 0;
235        } else {
236            return SystemClock.elapsedRealtime() - mHoldingStartTime;
237        }
238    }
239
240    @Override
241    public DisconnectCause getDisconnectCause() {
242        return mCause;
243    }
244
245    @Override
246    public boolean isIncoming() {
247        return mIsIncoming;
248    }
249
250    @Override
251    public GsmCall.State getState() {
252        if (mDisconnected) {
253            return GsmCall.State.DISCONNECTED;
254        } else {
255            return super.getState();
256        }
257    }
258
259    @Override
260    public void hangup() throws CallStateException {
261        if (!mDisconnected) {
262            mOwner.hangup(this);
263        } else {
264            throw new CallStateException ("disconnected");
265        }
266    }
267
268    @Override
269    public void separate() throws CallStateException {
270        if (!mDisconnected) {
271            mOwner.separate(this);
272        } else {
273            throw new CallStateException ("disconnected");
274        }
275    }
276
277    @Override
278    public PostDialState getPostDialState() {
279        return mPostDialState;
280    }
281
282    @Override
283    public void proceedAfterWaitChar() {
284        if (mPostDialState != PostDialState.WAIT) {
285            Rlog.w(LOG_TAG, "GsmConnection.proceedAfterWaitChar(): Expected "
286                + "getPostDialState() to be WAIT but was " + mPostDialState);
287            return;
288        }
289
290        setPostDialState(PostDialState.STARTED);
291
292        processNextPostDialChar();
293    }
294
295    @Override
296    public void proceedAfterWildChar(String str) {
297        if (mPostDialState != PostDialState.WILD) {
298            Rlog.w(LOG_TAG, "GsmConnection.proceedAfterWaitChar(): Expected "
299                + "getPostDialState() to be WILD but was " + mPostDialState);
300            return;
301        }
302
303        setPostDialState(PostDialState.STARTED);
304
305        // make a new postDialString, with the wild char replacement string
306        // at the beginning, followed by the remaining postDialString.
307
308        StringBuilder buf = new StringBuilder(str);
309        buf.append(mPostDialString.substring(mNextPostDialChar));
310        mPostDialString = buf.toString();
311        mNextPostDialChar = 0;
312        if (Phone.DEBUG_PHONE) {
313            log("proceedAfterWildChar: new postDialString is " +
314                    mPostDialString);
315        }
316
317        processNextPostDialChar();
318    }
319
320    @Override
321    public void cancelPostDial() {
322        setPostDialState(PostDialState.CANCELLED);
323    }
324
325    /**
326     * Called when this Connection is being hung up locally (eg, user pressed "end")
327     * Note that at this point, the hangup request has been dispatched to the radio
328     * but no response has yet been received so update() has not yet been called
329     */
330    void
331    onHangupLocal() {
332        mCause = DisconnectCause.LOCAL;
333    }
334
335    DisconnectCause
336    disconnectCauseFromCode(int causeCode) {
337        /**
338         * See 22.001 Annex F.4 for mapping of cause codes
339         * to local tones
340         */
341
342        switch (causeCode) {
343            case CallFailCause.USER_BUSY:
344                return DisconnectCause.BUSY;
345
346            case CallFailCause.NO_CIRCUIT_AVAIL:
347            case CallFailCause.TEMPORARY_FAILURE:
348            case CallFailCause.SWITCHING_CONGESTION:
349            case CallFailCause.CHANNEL_NOT_AVAIL:
350            case CallFailCause.QOS_NOT_AVAIL:
351            case CallFailCause.BEARER_NOT_AVAIL:
352                return DisconnectCause.CONGESTION;
353
354            case CallFailCause.ACM_LIMIT_EXCEEDED:
355                return DisconnectCause.LIMIT_EXCEEDED;
356
357            case CallFailCause.CALL_BARRED:
358                return DisconnectCause.CALL_BARRED;
359
360            case CallFailCause.FDN_BLOCKED:
361                return DisconnectCause.FDN_BLOCKED;
362
363            case CallFailCause.UNOBTAINABLE_NUMBER:
364                return DisconnectCause.UNOBTAINABLE_NUMBER;
365
366            case CallFailCause.ERROR_UNSPECIFIED:
367            case CallFailCause.NORMAL_CLEARING:
368            default:
369                GSMPhone phone = mOwner.mPhone;
370                int serviceState = phone.getServiceState().getState();
371                UiccCardApplication cardApp = UiccController
372                        .getInstance()
373                        .getUiccCardApplication(UiccController.APP_FAM_3GPP);
374                AppState uiccAppState = (cardApp != null) ? cardApp.getState() :
375                                                            AppState.APPSTATE_UNKNOWN;
376                if (serviceState == ServiceState.STATE_POWER_OFF) {
377                    return DisconnectCause.POWER_OFF;
378                } else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
379                        || serviceState == ServiceState.STATE_EMERGENCY_ONLY ) {
380                    return DisconnectCause.OUT_OF_SERVICE;
381                } else if (uiccAppState != AppState.APPSTATE_READY) {
382                    return DisconnectCause.ICC_ERROR;
383                } else if (causeCode == CallFailCause.ERROR_UNSPECIFIED) {
384                    if (phone.mSST.mRestrictedState.isCsRestricted()) {
385                        return DisconnectCause.CS_RESTRICTED;
386                    } else if (phone.mSST.mRestrictedState.isCsEmergencyRestricted()) {
387                        return DisconnectCause.CS_RESTRICTED_EMERGENCY;
388                    } else if (phone.mSST.mRestrictedState.isCsNormalRestricted()) {
389                        return DisconnectCause.CS_RESTRICTED_NORMAL;
390                    } else {
391                        return DisconnectCause.ERROR_UNSPECIFIED;
392                    }
393                } else if (causeCode == CallFailCause.NORMAL_CLEARING) {
394                    return DisconnectCause.NORMAL;
395                } else {
396                    // If nothing else matches, report unknown call drop reason
397                    // to app, not NORMAL call end.
398                    return DisconnectCause.ERROR_UNSPECIFIED;
399                }
400        }
401    }
402
403    /*package*/ void
404    onRemoteDisconnect(int causeCode) {
405        onDisconnect(disconnectCauseFromCode(causeCode));
406    }
407
408    /** Called when the radio indicates the connection has been disconnected */
409    /*package*/ boolean
410    onDisconnect(DisconnectCause cause) {
411        boolean changed = false;
412
413        mCause = cause;
414
415        if (!mDisconnected) {
416            mIndex = -1;
417
418            mDisconnectTime = System.currentTimeMillis();
419            mDuration = SystemClock.elapsedRealtime() - mConnectTimeReal;
420            mDisconnected = true;
421
422            if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
423
424            mOwner.mPhone.notifyDisconnect(this);
425
426            if (mParent != null) {
427                changed = mParent.connectionDisconnected(this);
428            }
429        }
430        releaseWakeLock();
431        return changed;
432    }
433
434    // Returns true if state has changed, false if nothing changed
435    /*package*/ boolean
436    update (DriverCall dc) {
437        GsmCall newParent;
438        boolean changed = false;
439        boolean wasConnectingInOrOut = isConnectingInOrOut();
440        boolean wasHolding = (getState() == GsmCall.State.HOLDING);
441
442        newParent = parentFromDCState(dc.state);
443
444        if (!equalsHandlesNulls(mAddress, dc.number)) {
445            if (Phone.DEBUG_PHONE) log("update: phone # changed!");
446            mAddress = dc.number;
447            changed = true;
448        }
449
450        // A null cnapName should be the same as ""
451        if (TextUtils.isEmpty(dc.name)) {
452            if (!TextUtils.isEmpty(mCnapName)) {
453                changed = true;
454                mCnapName = "";
455            }
456        } else if (!dc.name.equals(mCnapName)) {
457            changed = true;
458            mCnapName = dc.name;
459        }
460
461        if (Phone.DEBUG_PHONE) log("--dssds----"+mCnapName);
462        mCnapNamePresentation = dc.namePresentation;
463        mNumberPresentation = dc.numberPresentation;
464
465        if (newParent != mParent) {
466            if (mParent != null) {
467                mParent.detach(this);
468            }
469            newParent.attach(this, dc);
470            mParent = newParent;
471            changed = true;
472        } else {
473            boolean parentStateChange;
474            parentStateChange = mParent.update (this, dc);
475            changed = changed || parentStateChange;
476        }
477
478        /** Some state-transition events */
479
480        if (Phone.DEBUG_PHONE) log(
481                "update: parent=" + mParent +
482                ", hasNewParent=" + (newParent != mParent) +
483                ", wasConnectingInOrOut=" + wasConnectingInOrOut +
484                ", wasHolding=" + wasHolding +
485                ", isConnectingInOrOut=" + isConnectingInOrOut() +
486                ", changed=" + changed);
487
488
489        if (wasConnectingInOrOut && !isConnectingInOrOut()) {
490            onConnectedInOrOut();
491        }
492
493        if (changed && !wasHolding && (getState() == GsmCall.State.HOLDING)) {
494            // We've transitioned into HOLDING
495            onStartedHolding();
496        }
497
498        return changed;
499    }
500
501    /**
502     * Called when this Connection is in the foregroundCall
503     * when a dial is initiated.
504     * We know we're ACTIVE, and we know we're going to end up
505     * HOLDING in the backgroundCall
506     */
507    void
508    fakeHoldBeforeDial() {
509        if (mParent != null) {
510            mParent.detach(this);
511        }
512
513        mParent = mOwner.mBackgroundCall;
514        mParent.attachFake(this, GsmCall.State.HOLDING);
515
516        onStartedHolding();
517    }
518
519    /*package*/ int
520    getGSMIndex() throws CallStateException {
521        if (mIndex >= 0) {
522            return mIndex + 1;
523        } else {
524            throw new CallStateException ("GSM index not yet assigned");
525        }
526    }
527
528    /**
529     * An incoming or outgoing call has connected
530     */
531    void
532    onConnectedInOrOut() {
533        mConnectTime = System.currentTimeMillis();
534        mConnectTimeReal = SystemClock.elapsedRealtime();
535        mDuration = 0;
536
537        // bug #678474: incoming call interpreted as missed call, even though
538        // it sounds like the user has picked up the call.
539        if (Phone.DEBUG_PHONE) {
540            log("onConnectedInOrOut: connectTime=" + mConnectTime);
541        }
542
543        if (!mIsIncoming) {
544            // outgoing calls only
545            processNextPostDialChar();
546        }
547        releaseWakeLock();
548    }
549
550    /*package*/ void
551    onStartedHolding() {
552        mHoldingStartTime = SystemClock.elapsedRealtime();
553    }
554    /**
555     * Performs the appropriate action for a post-dial char, but does not
556     * notify application. returns false if the character is invalid and
557     * should be ignored
558     */
559    private boolean
560    processPostDialChar(char c) {
561        if (PhoneNumberUtils.is12Key(c)) {
562            mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
563        } else if (c == PhoneNumberUtils.PAUSE) {
564            // From TS 22.101:
565            // It continues...
566            // Upon the called party answering the UE shall send the DTMF digits
567            // automatically to the network after a delay of 3 seconds( 20 ).
568            // The digits shall be sent according to the procedures and timing
569            // specified in 3GPP TS 24.008 [13]. The first occurrence of the
570            // "DTMF Control Digits Separator" shall be used by the ME to
571            // distinguish between the addressing digits (i.e. the phone number)
572            // and the DTMF digits. Upon subsequent occurrences of the
573            // separator,
574            // the UE shall pause again for 3 seconds ( 20 ) before sending
575            // any further DTMF digits.
576            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_PAUSE_DONE),
577                    PAUSE_DELAY_MILLIS);
578        } else if (c == PhoneNumberUtils.WAIT) {
579            setPostDialState(PostDialState.WAIT);
580        } else if (c == PhoneNumberUtils.WILD) {
581            setPostDialState(PostDialState.WILD);
582        } else {
583            return false;
584        }
585
586        return true;
587    }
588
589    @Override
590    public String
591    getRemainingPostDialString() {
592        if (mPostDialState == PostDialState.CANCELLED
593            || mPostDialState == PostDialState.COMPLETE
594            || mPostDialString == null
595            || mPostDialString.length() <= mNextPostDialChar
596        ) {
597            return "";
598        }
599
600        return mPostDialString.substring(mNextPostDialChar);
601    }
602
603    @Override
604    protected void finalize()
605    {
606        /**
607         * It is understood that This finializer is not guaranteed
608         * to be called and the release lock call is here just in
609         * case there is some path that doesn't call onDisconnect
610         * and or onConnectedInOrOut.
611         */
612        if (mPartialWakeLock.isHeld()) {
613            Rlog.e(LOG_TAG, "[GSMConn] UNEXPECTED; mPartialWakeLock is held when finalizing.");
614        }
615        releaseWakeLock();
616    }
617
618    private void
619    processNextPostDialChar() {
620        char c = 0;
621        Registrant postDialHandler;
622
623        if (mPostDialState == PostDialState.CANCELLED) {
624            //Rlog.v("GSM", "##### processNextPostDialChar: postDialState == CANCELLED, bail");
625            return;
626        }
627
628        if (mPostDialString == null ||
629                mPostDialString.length() <= mNextPostDialChar) {
630            setPostDialState(PostDialState.COMPLETE);
631
632            // notifyMessage.arg1 is 0 on complete
633            c = 0;
634        } else {
635            boolean isValid;
636
637            setPostDialState(PostDialState.STARTED);
638
639            c = mPostDialString.charAt(mNextPostDialChar++);
640
641            isValid = processPostDialChar(c);
642
643            if (!isValid) {
644                // Will call processNextPostDialChar
645                mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
646                // Don't notify application
647                Rlog.e("GSM", "processNextPostDialChar: c=" + c + " isn't valid!");
648                return;
649            }
650        }
651
652        postDialHandler = mOwner.mPhone.mPostDialHandler;
653
654        Message notifyMessage;
655
656        if (postDialHandler != null
657                && (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
658            // The AsyncResult.result is the Connection object
659            PostDialState state = mPostDialState;
660            AsyncResult ar = AsyncResult.forMessage(notifyMessage);
661            ar.result = this;
662            ar.userObj = state;
663
664            // arg1 is the character that was/is being processed
665            notifyMessage.arg1 = c;
666
667            //Rlog.v("GSM", "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
668            notifyMessage.sendToTarget();
669        }
670    }
671
672
673    /** "connecting" means "has never been ACTIVE" for both incoming
674     *  and outgoing calls
675     */
676    private boolean
677    isConnectingInOrOut() {
678        return mParent == null || mParent == mOwner.mRingingCall
679            || mParent.mState == GsmCall.State.DIALING
680            || mParent.mState == GsmCall.State.ALERTING;
681    }
682
683    private GsmCall
684    parentFromDCState (DriverCall.State state) {
685        switch (state) {
686            case ACTIVE:
687            case DIALING:
688            case ALERTING:
689                return mOwner.mForegroundCall;
690            //break;
691
692            case HOLDING:
693                return mOwner.mBackgroundCall;
694            //break;
695
696            case INCOMING:
697            case WAITING:
698                return mOwner.mRingingCall;
699            //break;
700
701            default:
702                throw new RuntimeException("illegal call state: " + state);
703        }
704    }
705
706    /**
707     * Set post dial state and acquire wake lock while switching to "started"
708     * state, the wake lock will be released if state switches out of "started"
709     * state or after WAKE_LOCK_TIMEOUT_MILLIS.
710     * @param s new PostDialState
711     */
712    private void setPostDialState(PostDialState s) {
713        if (mPostDialState != PostDialState.STARTED
714                && s == PostDialState.STARTED) {
715            acquireWakeLock();
716            Message msg = mHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
717            mHandler.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS);
718        } else if (mPostDialState == PostDialState.STARTED
719                && s != PostDialState.STARTED) {
720            mHandler.removeMessages(EVENT_WAKE_LOCK_TIMEOUT);
721            releaseWakeLock();
722        }
723        mPostDialState = s;
724    }
725
726    private void
727    createWakeLock(Context context) {
728        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
729        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
730    }
731
732    private void
733    acquireWakeLock() {
734        log("acquireWakeLock");
735        mPartialWakeLock.acquire();
736    }
737
738    private void
739    releaseWakeLock() {
740        synchronized(mPartialWakeLock) {
741            if (mPartialWakeLock.isHeld()) {
742                log("releaseWakeLock");
743                mPartialWakeLock.release();
744            }
745        }
746    }
747
748    private void log(String msg) {
749        Rlog.d(LOG_TAG, "[GSMConn] " + msg);
750    }
751
752    @Override
753    public int getNumberPresentation() {
754        return mNumberPresentation;
755    }
756
757    @Override
758    public UUSInfo getUUSInfo() {
759        return mUusInfo;
760    }
761}
762