1/*
2 * Copyright (C) 2007 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 */
16package com.android.internal.telephony;
17
18import java.io.FileDescriptor;
19import java.io.PrintWriter;
20
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.os.Binder;
24import android.os.RemoteException;
25import android.telephony.PhoneNumberUtils;
26import android.telephony.Rlog;
27
28import com.android.internal.telephony.uicc.IsimRecords;
29import com.android.internal.telephony.uicc.UiccCard;
30import com.android.internal.telephony.uicc.UiccCardApplication;
31
32public class PhoneSubInfo {
33    static final String LOG_TAG = "PhoneSubInfo";
34    private static final boolean DBG = true;
35    private static final boolean VDBG = false; // STOPSHIP if true
36
37    private Phone mPhone;
38    private Context mContext;
39    private static final String READ_PHONE_STATE =
40        android.Manifest.permission.READ_PHONE_STATE;
41    // TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
42    private static final String CALL_PRIVILEGED =
43        android.Manifest.permission.CALL_PRIVILEGED;
44    private static final String READ_PRIVILEGED_PHONE_STATE =
45        android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
46
47    public PhoneSubInfo(Phone phone) {
48        mPhone = phone;
49        mContext = phone.getContext();
50    }
51
52    public void dispose() {
53    }
54
55    @Override
56    protected void finalize() {
57        try {
58            super.finalize();
59        } catch (Throwable throwable) {
60            loge("Error while finalizing:", throwable);
61        }
62        if (DBG) log("PhoneSubInfo finalized");
63    }
64
65    /**
66     * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
67     */
68    public String getDeviceId() {
69        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
70        return mPhone.getDeviceId();
71    }
72
73    /**
74     * Retrieves the IMEI.
75     */
76    public String getImei() {
77        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
78        return mPhone.getImei();
79    }
80
81    /**
82     * Retrieves the NAI.
83     */
84    public String getNai() {
85        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
86        return mPhone.getNai();
87    }
88
89    /**
90     * Retrieves the software version number for the device, e.g., IMEI/SV
91     * for GSM phones.
92     */
93    public String getDeviceSvn() {
94        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
95        return mPhone.getDeviceSvn();
96    }
97
98    /**
99     * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
100     */
101    public String getSubscriberId() {
102        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
103        return mPhone.getSubscriberId();
104    }
105
106    /**
107     * Retrieves the Group Identifier Level1 for GSM phones.
108     */
109    public String getGroupIdLevel1() {
110        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
111        return mPhone.getGroupIdLevel1();
112    }
113
114    /**
115     * Retrieves the serial number of the ICC, if applicable.
116     */
117    public String getIccSerialNumber() {
118        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
119        return mPhone.getIccSerialNumber();
120    }
121
122    /**
123     * Retrieves the phone number string for line 1.
124     */
125    public String getLine1Number() {
126        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
127        return mPhone.getLine1Number();
128    }
129
130    /**
131     * Retrieves the alpha identifier for line 1.
132     */
133    public String getLine1AlphaTag() {
134        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
135        return mPhone.getLine1AlphaTag();
136    }
137
138    /**
139     * Retrieves the MSISDN string.
140     */
141    public String getMsisdn() {
142        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
143        return mPhone.getMsisdn();
144    }
145
146    /**
147     * Retrieves the voice mail number.
148     */
149    public String getVoiceMailNumber() {
150        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
151        String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
152        if (VDBG) log("VM: PhoneSubInfo.getVoiceMailNUmber: " + number);
153        return number;
154    }
155
156    /**
157     * Retrieves the complete voice mail number.
158     *
159     * @hide
160     */
161    public String getCompleteVoiceMailNumber() {
162        mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
163                "Requires CALL_PRIVILEGED");
164        String number = mPhone.getVoiceMailNumber();
165        if (VDBG) log("VM: PhoneSubInfo.getCompleteVoiceMailNUmber: " + number);
166        return number;
167    }
168
169    /**
170     * Retrieves the alpha identifier associated with the voice mail number.
171     */
172    public String getVoiceMailAlphaTag() {
173        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
174        return mPhone.getVoiceMailAlphaTag();
175    }
176
177    /**
178     * Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
179     * @return the IMPI, or null if not present or not loaded
180     */
181    public String getIsimImpi() {
182        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
183                "Requires READ_PRIVILEGED_PHONE_STATE");
184        IsimRecords isim = mPhone.getIsimRecords();
185        if (isim != null) {
186            return isim.getIsimImpi();
187        } else {
188            return null;
189        }
190    }
191
192    /**
193     * Returns the IMS home network domain name that was loaded from the ISIM.
194     * @return the IMS domain name, or null if not present or not loaded
195     */
196    public String getIsimDomain() {
197        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
198                "Requires READ_PRIVILEGED_PHONE_STATE");
199        IsimRecords isim = mPhone.getIsimRecords();
200        if (isim != null) {
201            return isim.getIsimDomain();
202        } else {
203            return null;
204        }
205    }
206
207    /**
208     * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
209     * @return an array of IMPU strings, with one IMPU per string, or null if
210     *      not present or not loaded
211     */
212    public String[] getIsimImpu() {
213        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
214                "Requires READ_PRIVILEGED_PHONE_STATE");
215        IsimRecords isim = mPhone.getIsimRecords();
216        if (isim != null) {
217            return isim.getIsimImpu();
218        } else {
219            return null;
220        }
221    }
222
223    /**
224     * Returns the IMS Service Table (IST) that was loaded from the ISIM.
225     * @return IMS Service Table or null if not present or not loaded
226     */
227    public String getIsimIst(){
228        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
229                "Requires READ_PRIVILEGED_PHONE_STATE");
230        IsimRecords isim = mPhone.getIsimRecords();
231        if (isim != null) {
232            return isim.getIsimIst();
233        } else {
234            return null;
235        }
236     }
237
238    /**
239     * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
240     * @return an array of  PCSCF strings with one PCSCF per string, or null if
241     *      not present or not loaded
242     */
243    public String[] getIsimPcscf() {
244        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
245                "Requires READ_PRIVILEGED_PHONE_STATE");
246        IsimRecords isim = mPhone.getIsimRecords();
247        if (isim != null) {
248            return isim.getIsimPcscf();
249        } else {
250            return null;
251        }
252    }
253
254    /**
255     * Returns the response of ISIM Authetification through RIL.
256     * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
257     * @return the response of ISIM Authetification, or null if not available
258     */
259    public String getIsimChallengeResponse(String nonce){
260        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
261                "Requires READ_PRIVILEGED_PHONE_STATE");
262        IsimRecords isim = mPhone.getIsimRecords();
263        if (isim != null) {
264            return isim.getIsimChallengeResponse(nonce);
265        } else {
266            return null;
267        }
268    }
269
270    /**
271     * Returns the response of the SIM application on the UICC to authentication
272     * challenge/response algorithm. The data string and challenge response are
273     * Base64 encoded Strings.
274     * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
275     *
276     * @param appType ICC application family (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
277     * @param data authentication challenge data
278     * @return challenge response
279     */
280    public String getIccSimChallengeResponse(int subId, int appType, String data) {
281        // FIXME: use subId!!
282        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
283                "Requires READ_PRIVILEGED_PHONE_STATE");
284
285        UiccCard uiccCard = mPhone.getUiccCard();
286        if (uiccCard == null) {
287            Rlog.e(LOG_TAG, "getIccSimChallengeResponse() UiccCard is null");
288            return null;
289        }
290
291        UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType);
292        if (uiccApp == null) {
293            Rlog.e(LOG_TAG, "getIccSimChallengeResponse() no app with specified type -- " +
294                    appType);
295            return null;
296        } else {
297            Rlog.e(LOG_TAG, "getIccSimChallengeResponse() found app " + uiccApp.getAid()
298                    + "specified type -- " + appType);
299        }
300
301        int authContext = uiccApp.getAuthContext();
302
303        if (data.length() < 32) {
304            /* must use EAP_SIM context */
305            Rlog.e(LOG_TAG, "data is too small to use EAP_AKA, using EAP_SIM instead");
306            authContext = UiccCardApplication.AUTH_CONTEXT_EAP_SIM;
307        }
308
309        if(authContext == UiccCardApplication.AUTH_CONTEXT_UNDEFINED) {
310            Rlog.e(LOG_TAG, "getIccSimChallengeResponse() authContext undefined for app type " +
311                    appType);
312            return null;
313        }
314
315        return uiccApp.getIccRecords().getIccSimChallengeResponse(authContext, data);
316    }
317
318    private void log(String s) {
319        Rlog.d(LOG_TAG, s);
320    }
321
322    private void loge(String s, Throwable e) {
323        Rlog.e(LOG_TAG, s, e);
324    }
325
326    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
327        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
328                != PackageManager.PERMISSION_GRANTED) {
329            pw.println("Permission Denial: can't dump PhoneSubInfo from from pid="
330                    + Binder.getCallingPid()
331                    + ", uid=" + Binder.getCallingUid());
332            return;
333        }
334
335        pw.println("Phone Subscriber Info:");
336        pw.println("  Phone Type = " + mPhone.getPhoneName());
337        pw.println("  Device ID = " + mPhone.getDeviceId());
338    }
339}
340