1/*
2 * Copyright (C) 2010 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.sip;
18
19import android.content.Context;
20import android.os.AsyncResult;
21import android.os.Handler;
22import android.os.Message;
23import android.os.Registrant;
24import android.os.RegistrantList;
25import android.os.SystemProperties;
26import android.telephony.CellLocation;
27import android.telephony.ServiceState;
28import android.telephony.SignalStrength;
29import android.util.Log;
30
31import com.android.internal.telephony.Call;
32import com.android.internal.telephony.CallStateException;
33import com.android.internal.telephony.Connection;
34import com.android.internal.telephony.DataConnection;
35import com.android.internal.telephony.IccCard;
36import com.android.internal.telephony.IccFileHandler;
37import com.android.internal.telephony.IccPhoneBookInterfaceManager;
38import com.android.internal.telephony.IccSmsInterfaceManager;
39import com.android.internal.telephony.MmiCode;
40import com.android.internal.telephony.Phone;
41import com.android.internal.telephony.PhoneBase;
42import com.android.internal.telephony.PhoneNotifier;
43import com.android.internal.telephony.PhoneSubInfo;
44import com.android.internal.telephony.TelephonyProperties;
45
46import java.util.ArrayList;
47import java.util.List;
48
49abstract class SipPhoneBase extends PhoneBase {
50    private static final String LOG_TAG = "SipPhone";
51
52    private RegistrantList mRingbackRegistrants = new RegistrantList();
53    private State state = State.IDLE;
54
55    public SipPhoneBase(Context context, PhoneNotifier notifier) {
56        super(notifier, context, new SipCommandInterface(context), false);
57    }
58
59    public abstract Call getForegroundCall();
60
61    public abstract Call getBackgroundCall();
62
63    public abstract Call getRingingCall();
64
65    void migrateFrom(SipPhoneBase from) {
66        migrate(mRingbackRegistrants, from.mRingbackRegistrants);
67        migrate(mPreciseCallStateRegistrants, from.mPreciseCallStateRegistrants);
68        migrate(mNewRingingConnectionRegistrants, from.mNewRingingConnectionRegistrants);
69        migrate(mIncomingRingRegistrants, from.mIncomingRingRegistrants);
70        migrate(mDisconnectRegistrants, from.mDisconnectRegistrants);
71        migrate(mServiceStateRegistrants, from.mServiceStateRegistrants);
72        migrate(mMmiCompleteRegistrants, from.mMmiCompleteRegistrants);
73        migrate(mMmiRegistrants, from.mMmiRegistrants);
74        migrate(mUnknownConnectionRegistrants, from.mUnknownConnectionRegistrants);
75        migrate(mSuppServiceFailedRegistrants, from.mSuppServiceFailedRegistrants);
76    }
77
78    static void migrate(RegistrantList to, RegistrantList from) {
79        from.removeCleared();
80        for (int i = 0, n = from.size(); i < n; i++) {
81            to.add((Registrant) from.get(i));
82        }
83    }
84
85    @Override
86    public void registerForRingbackTone(Handler h, int what, Object obj) {
87        mRingbackRegistrants.addUnique(h, what, obj);
88    }
89
90    @Override
91    public void unregisterForRingbackTone(Handler h) {
92        mRingbackRegistrants.remove(h);
93    }
94
95    protected void startRingbackTone() {
96        AsyncResult result = new AsyncResult(null, new Boolean(true), null);
97        mRingbackRegistrants.notifyRegistrants(result);
98    }
99
100    protected void stopRingbackTone() {
101        AsyncResult result = new AsyncResult(null, new Boolean(false), null);
102        mRingbackRegistrants.notifyRegistrants(result);
103    }
104
105    public ServiceState getServiceState() {
106        // FIXME: we may need to provide this when data connectivity is lost
107        // or when server is down
108        ServiceState s = new ServiceState();
109        s.setState(ServiceState.STATE_IN_SERVICE);
110        return s;
111    }
112
113    public CellLocation getCellLocation() {
114        return null;
115    }
116
117    public State getState() {
118        return state;
119    }
120
121    public int getPhoneType() {
122        return Phone.PHONE_TYPE_SIP;
123    }
124
125    public SignalStrength getSignalStrength() {
126        return new SignalStrength();
127    }
128
129    public boolean getMessageWaitingIndicator() {
130        return false;
131    }
132
133    public boolean getCallForwardingIndicator() {
134        return false;
135    }
136
137    public List<? extends MmiCode> getPendingMmiCodes() {
138        return new ArrayList<MmiCode>(0);
139    }
140
141    public DataState getDataConnectionState() {
142        return DataState.DISCONNECTED;
143    }
144
145    public DataState getDataConnectionState(String apnType) {
146        return DataState.DISCONNECTED;
147    }
148
149    public DataActivityState getDataActivityState() {
150        return DataActivityState.NONE;
151    }
152
153    /**
154     * Notify any interested party of a Phone state change {@link Phone.State}
155     */
156    void notifyPhoneStateChanged() {
157        mNotifier.notifyPhoneState(this);
158    }
159
160    /**
161     * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
162     * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
163     */
164    void notifyPreciseCallStateChanged() {
165        /* we'd love it if this was package-scoped*/
166        super.notifyPreciseCallStateChangedP();
167    }
168
169    void notifyNewRingingConnection(Connection c) {
170        super.notifyNewRingingConnectionP(c);
171    }
172
173    void notifyDisconnect(Connection cn) {
174        mDisconnectRegistrants.notifyResult(cn);
175    }
176
177    void notifyUnknownConnection() {
178        mUnknownConnectionRegistrants.notifyResult(this);
179    }
180
181    void notifySuppServiceFailed(SuppService code) {
182        mSuppServiceFailedRegistrants.notifyResult(code);
183    }
184
185    void notifyServiceStateChanged(ServiceState ss) {
186        super.notifyServiceStateChangedP(ss);
187    }
188
189    public void notifyCallForwardingIndicator() {
190        mNotifier.notifyCallForwardingChanged(this);
191    }
192
193    public boolean canDial() {
194        int serviceState = getServiceState().getState();
195        Log.v(LOG_TAG, "canDial(): serviceState = " + serviceState);
196        if (serviceState == ServiceState.STATE_POWER_OFF) return false;
197
198        String disableCall = SystemProperties.get(
199                TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
200        Log.v(LOG_TAG, "canDial(): disableCall = " + disableCall);
201        if (disableCall.equals("true")) return false;
202
203        Log.v(LOG_TAG, "canDial(): ringingCall: " + getRingingCall().getState());
204        Log.v(LOG_TAG, "canDial(): foregndCall: " + getForegroundCall().getState());
205        Log.v(LOG_TAG, "canDial(): backgndCall: " + getBackgroundCall().getState());
206        return !getRingingCall().isRinging()
207                && (!getForegroundCall().getState().isAlive()
208                    || !getBackgroundCall().getState().isAlive());
209    }
210
211    public boolean handleInCallMmiCommands(String dialString)
212            throws CallStateException {
213        return false;
214    }
215
216    boolean isInCall() {
217        Call.State foregroundCallState = getForegroundCall().getState();
218        Call.State backgroundCallState = getBackgroundCall().getState();
219        Call.State ringingCallState = getRingingCall().getState();
220
221       return (foregroundCallState.isAlive() || backgroundCallState.isAlive()
222            || ringingCallState.isAlive());
223    }
224
225    public boolean handlePinMmi(String dialString) {
226        return false;
227    }
228
229    public void sendUssdResponse(String ussdMessge) {
230    }
231
232    public void registerForSuppServiceNotification(
233            Handler h, int what, Object obj) {
234    }
235
236    public void unregisterForSuppServiceNotification(Handler h) {
237    }
238
239    public void setRadioPower(boolean power) {
240    }
241
242    public String getVoiceMailNumber() {
243        return null;
244    }
245
246    public String getVoiceMailAlphaTag() {
247        return null;
248    }
249
250    public String getDeviceId() {
251        return null;
252    }
253
254    public String getDeviceSvn() {
255        return null;
256    }
257
258    public String getEsn() {
259        Log.e(LOG_TAG, "[SipPhone] getEsn() is a CDMA method");
260        return "0";
261    }
262
263    public String getMeid() {
264        Log.e(LOG_TAG, "[SipPhone] getMeid() is a CDMA method");
265        return "0";
266    }
267
268    public String getSubscriberId() {
269        return null;
270    }
271
272    public String getIccSerialNumber() {
273        return null;
274    }
275
276    public String getLine1Number() {
277        return null;
278    }
279
280    public String getLine1AlphaTag() {
281        return null;
282    }
283
284    public void setLine1Number(String alphaTag, String number, Message onComplete) {
285        // FIXME: what to reply for SIP?
286        AsyncResult.forMessage(onComplete, null, null);
287        onComplete.sendToTarget();
288    }
289
290    public void setVoiceMailNumber(String alphaTag, String voiceMailNumber,
291            Message onComplete) {
292        // FIXME: what to reply for SIP?
293        AsyncResult.forMessage(onComplete, null, null);
294        onComplete.sendToTarget();
295    }
296
297    public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
298    }
299
300    public void setCallForwardingOption(int commandInterfaceCFAction,
301            int commandInterfaceCFReason, String dialingNumber,
302            int timerSeconds, Message onComplete) {
303    }
304
305    public void getOutgoingCallerIdDisplay(Message onComplete) {
306        // FIXME: what to reply?
307        AsyncResult.forMessage(onComplete, null, null);
308        onComplete.sendToTarget();
309    }
310
311    public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
312                                           Message onComplete) {
313        // FIXME: what's this for SIP?
314        AsyncResult.forMessage(onComplete, null, null);
315        onComplete.sendToTarget();
316    }
317
318    public void getCallWaiting(Message onComplete) {
319        AsyncResult.forMessage(onComplete, null, null);
320        onComplete.sendToTarget();
321    }
322
323    public void setCallWaiting(boolean enable, Message onComplete) {
324        Log.e(LOG_TAG, "call waiting not supported");
325    }
326
327    public boolean getIccRecordsLoaded() {
328        return false;
329    }
330
331    public IccCard getIccCard() {
332        return null;
333    }
334
335    public void getAvailableNetworks(Message response) {
336    }
337
338    public void setNetworkSelectionModeAutomatic(Message response) {
339    }
340
341    public void selectNetworkManually(
342            com.android.internal.telephony.gsm.NetworkInfo network,
343            Message response) {
344    }
345
346    public void getNeighboringCids(Message response) {
347    }
348
349    public void setOnPostDialCharacter(Handler h, int what, Object obj) {
350    }
351
352    public void getDataCallList(Message response) {
353    }
354
355    public List<DataConnection> getCurrentDataConnectionList () {
356        return null;
357    }
358
359    public void updateServiceLocation() {
360    }
361
362    public void enableLocationUpdates() {
363    }
364
365    public void disableLocationUpdates() {
366    }
367
368    public boolean getDataRoamingEnabled() {
369        return false;
370    }
371
372    public void setDataRoamingEnabled(boolean enable) {
373    }
374
375    public boolean enableDataConnectivity() {
376        return false;
377    }
378
379    public boolean disableDataConnectivity() {
380        return false;
381    }
382
383    public boolean isDataConnectivityPossible() {
384        return false;
385    }
386
387    boolean updateCurrentCarrierInProvider() {
388        return false;
389    }
390
391    public void saveClirSetting(int commandInterfaceCLIRMode) {
392    }
393
394    public PhoneSubInfo getPhoneSubInfo(){
395        return null;
396    }
397
398    public IccSmsInterfaceManager getIccSmsInterfaceManager(){
399        return null;
400    }
401
402    public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
403        return null;
404    }
405
406    public IccFileHandler getIccFileHandler(){
407        return null;
408    }
409
410    public void activateCellBroadcastSms(int activate, Message response) {
411        Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
412    }
413
414    public void getCellBroadcastSmsConfig(Message response) {
415        Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
416    }
417
418    public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){
419        Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
420    }
421
422    void updatePhoneState() {
423        State oldState = state;
424
425        if (getRingingCall().isRinging()) {
426            state = State.RINGING;
427        } else if (getForegroundCall().isIdle()
428                && getBackgroundCall().isIdle()) {
429            state = State.IDLE;
430        } else {
431            state = State.OFFHOOK;
432        }
433
434        if (state != oldState) {
435            Log.d(LOG_TAG, " ^^^ new phone state: " + state);
436            notifyPhoneStateChanged();
437        }
438    }
439}
440