1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
4 * Not a Contribution.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19package com.android.internal.telephony;
20
21import android.app.AppOpsManager;
22import android.content.Context;
23import android.content.pm.PackageManager;
24import android.os.Binder;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.telephony.PhoneNumberUtils;
28import android.telephony.SubscriptionManager;
29import android.telephony.Rlog;
30
31import com.android.internal.telephony.uicc.IsimRecords;
32import com.android.internal.telephony.uicc.UiccCard;
33import com.android.internal.telephony.uicc.UiccCardApplication;
34
35import static android.Manifest.permission.CALL_PRIVILEGED;
36import static android.Manifest.permission.READ_PHONE_STATE;
37import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
38import static android.Manifest.permission.READ_SMS;
39import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
40
41public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
42    private static final String TAG = "PhoneSubInfoController";
43    private static final boolean DBG = true;
44    private static final boolean VDBG = false; // STOPSHIP if true
45
46    private final Phone[] mPhone;
47    private final Context mContext;
48    private final AppOpsManager mAppOps;
49
50    public PhoneSubInfoController(Context context, Phone[] phone) {
51        mPhone = phone;
52        if (ServiceManager.getService("iphonesubinfo") == null) {
53            ServiceManager.addService("iphonesubinfo", this);
54        }
55        mContext = context;
56        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
57    }
58
59    public String getDeviceId(String callingPackage) {
60        return getDeviceIdForPhone(SubscriptionManager.getPhoneId(getDefaultSubscription()),
61                callingPackage);
62    }
63
64    public String getDeviceIdForPhone(int phoneId, String callingPackage) {
65        if (!checkReadPhoneState(callingPackage, "getDeviceId")) {
66            return null;
67        }
68        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
69            phoneId = 0;
70        }
71        final Phone phone = mPhone[phoneId];
72        if (phone != null) {
73            phone.getContext().enforceCallingOrSelfPermission(
74                    android.Manifest.permission.READ_PHONE_STATE,
75                    "Requires READ_PHONE_STATE");
76            return phone.getDeviceId();
77        } else {
78            loge("getDeviceIdForPhone phone " + phoneId + " is null");
79            return null;
80        }
81    }
82
83    public String getNaiForSubscriber(int subId, String callingPackage) {
84        Phone phone = getPhone(subId);
85        if (phone != null) {
86            if (!checkReadPhoneState(callingPackage, "getNai")) {
87                return null;
88            }
89            return phone.getNai();
90        } else {
91            loge("getNai phone is null for Subscription:" + subId);
92            return null;
93        }
94    }
95
96    public String getImeiForSubscriber(int subId, String callingPackage) {
97        Phone phone = getPhone(subId);
98        if (phone != null) {
99            if (!checkReadPhoneState(callingPackage, "getImei")) {
100                return null;
101            }
102            return phone.getImei();
103        } else {
104            loge("getDeviceId phone is null for Subscription:" + subId);
105            return null;
106        }
107    }
108
109    public String getDeviceSvn(String callingPackage) {
110        return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
111    }
112
113    public String getDeviceSvnUsingSubId(int subId, String callingPackage) {
114        Phone phone = getPhone(subId);
115        if (phone != null) {
116            if (!checkReadPhoneState(callingPackage, "getDeviceSvn")) {
117                return null;
118            }
119            return phone.getDeviceSvn();
120        } else {
121            loge("getDeviceSvn phone is null");
122            return null;
123        }
124    }
125
126    public String getSubscriberId(String callingPackage) {
127        return getSubscriberIdForSubscriber(getDefaultSubscription(), callingPackage);
128    }
129
130    public String getSubscriberIdForSubscriber(int subId, String callingPackage) {
131        Phone phone = getPhone(subId);
132        if (phone != null) {
133            if (!checkReadPhoneState(callingPackage, "getSubscriberId")) {
134                return null;
135            }
136            return phone.getSubscriberId();
137        } else {
138            loge("getSubscriberId phone is null for Subscription:" + subId);
139            return null;
140        }
141    }
142
143    /**
144     * Retrieves the serial number of the ICC, if applicable.
145     */
146    public String getIccSerialNumber(String callingPackage) {
147        return getIccSerialNumberForSubscriber(getDefaultSubscription(), callingPackage);
148    }
149
150    public String getIccSerialNumberForSubscriber(int subId, String callingPackage) {
151        Phone phone = getPhone(subId);
152        if (phone != null) {
153            if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {
154                return null;
155            }
156            return phone.getIccSerialNumber();
157        } else {
158            loge("getIccSerialNumber phone is null for Subscription:" + subId);
159            return null;
160        }
161    }
162
163    public String getLine1Number(String callingPackage) {
164        return getLine1NumberForSubscriber(getDefaultSubscription(), callingPackage);
165    }
166
167    public String getLine1NumberForSubscriber(int subId, String callingPackage) {
168        Phone phone = getPhone(subId);
169        if (phone != null) {
170            // This is open to apps with WRITE_SMS.
171            if (!checkReadPhoneNumber(callingPackage, "getLine1Number")) {
172                return null;
173            }
174            return phone.getLine1Number();
175        } else {
176            loge("getLine1Number phone is null for Subscription:" + subId);
177            return null;
178        }
179    }
180
181    public String getLine1AlphaTag(String callingPackage) {
182        return getLine1AlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
183    }
184
185    public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) {
186        Phone phone = getPhone(subId);
187        if (phone != null) {
188            if (!checkReadPhoneState(callingPackage, "getLine1AlphaTag")) {
189                return null;
190            }
191            return phone.getLine1AlphaTag();
192        } else {
193            loge("getLine1AlphaTag phone is null for Subscription:" + subId);
194            return null;
195        }
196    }
197
198    public String getMsisdn(String callingPackage) {
199        return getMsisdnForSubscriber(getDefaultSubscription(), callingPackage);
200    }
201
202    public String getMsisdnForSubscriber(int subId, String callingPackage) {
203        Phone phone = getPhone(subId);
204        if (phone != null) {
205            if (!checkReadPhoneState(callingPackage, "getMsisdn")) {
206                return null;
207            }
208            return phone.getMsisdn();
209        } else {
210            loge("getMsisdn phone is null for Subscription:" + subId);
211            return null;
212        }
213    }
214
215    public String getVoiceMailNumber(String callingPackage) {
216        return getVoiceMailNumberForSubscriber(getDefaultSubscription(), callingPackage);
217    }
218
219    public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) {
220        Phone phone = getPhone(subId);
221        if (phone != null) {
222            if (!checkReadPhoneState(callingPackage, "getVoiceMailNumber")) {
223                return null;
224            }
225            String number = PhoneNumberUtils.extractNetworkPortion(phone.getVoiceMailNumber());
226            if (VDBG) log("VM: getVoiceMailNUmber: " + number);
227            return number;
228        } else {
229            loge("getVoiceMailNumber phone is null for Subscription:" + subId);
230            return null;
231        }
232    }
233
234    // TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
235    public String getCompleteVoiceMailNumber() {
236        return getCompleteVoiceMailNumberForSubscriber(getDefaultSubscription());
237    }
238
239    public String getCompleteVoiceMailNumberForSubscriber(int subId) {
240        Phone phone = getPhone(subId);
241        if (phone != null) {
242            mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED, "Requires CALL_PRIVILEGED");
243            String number = phone.getVoiceMailNumber();
244            if (VDBG) log("VM: getCompleteVoiceMailNUmber: " + number);
245            return number;
246        } else {
247            loge("getCompleteVoiceMailNumber phone is null for Subscription:" + subId);
248            return null;
249        }
250    }
251
252    public String getVoiceMailAlphaTag(String callingPackage) {
253        return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
254    }
255
256    public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) {
257        Phone phone = getPhone(subId);
258        if (phone != null) {
259            if (!checkReadPhoneState(callingPackage, "getVoiceMailAlphaTag")) {
260                return null;
261            }
262            return phone.getVoiceMailAlphaTag();
263        } else {
264            loge("getVoiceMailAlphaTag phone is null for Subscription:" + subId);
265            return null;
266        }
267    }
268
269    /**
270     * get Phone object based on subId.
271     **/
272    private Phone getPhone(int subId) {
273        int phoneId = SubscriptionManager.getPhoneId(subId);
274        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
275            phoneId = 0;
276        }
277        return mPhone[phoneId];
278    }
279
280    /**
281     * Make sure caller has either read privileged phone permission or carrier privilege.
282     *
283     * @throws SecurityException if the caller does not have the required permission/privilege
284     */
285    private void enforcePrivilegedPermissionOrCarrierPrivilege(Phone phone) {
286        int permissionResult = mContext.checkCallingOrSelfPermission(
287                READ_PRIVILEGED_PHONE_STATE);
288        if (permissionResult == PackageManager.PERMISSION_GRANTED) {
289            return;
290        }
291        log("No read privileged phone permission, check carrier privilege next.");
292        UiccCard uiccCard = phone.getUiccCard();
293        if (uiccCard == null) {
294            throw new SecurityException("No Carrier Privilege: No UICC");
295        }
296        if (uiccCard.getCarrierPrivilegeStatusForCurrentTransaction(
297                mContext.getPackageManager()) != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
298            throw new SecurityException("No Carrier Privilege.");
299        }
300    }
301
302    private int getDefaultSubscription() {
303        return  PhoneFactory.getDefaultSubscription();
304    }
305
306
307    public String getIsimImpi() {
308        Phone phone = getPhone(getDefaultSubscription());
309        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
310                "Requires READ_PRIVILEGED_PHONE_STATE");
311        IsimRecords isim = phone.getIsimRecords();
312        if (isim != null) {
313            return isim.getIsimImpi();
314        } else {
315            return null;
316        }
317    }
318
319    public String getIsimDomain() {
320        Phone phone = getPhone(getDefaultSubscription());
321        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
322                "Requires READ_PRIVILEGED_PHONE_STATE");
323        IsimRecords isim = phone.getIsimRecords();
324        if (isim != null) {
325            return isim.getIsimDomain();
326        } else {
327            return null;
328        }
329    }
330
331    public String[] getIsimImpu() {
332        Phone phone = getPhone(getDefaultSubscription());
333        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
334                "Requires READ_PRIVILEGED_PHONE_STATE");
335        IsimRecords isim = phone.getIsimRecords();
336        if (isim != null) {
337            return isim.getIsimImpu();
338        } else {
339            return null;
340        }
341    }
342
343    public String getIsimIst() throws RemoteException {
344        Phone phone = getPhone(getDefaultSubscription());
345        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
346                "Requires READ_PRIVILEGED_PHONE_STATE");
347        IsimRecords isim = phone.getIsimRecords();
348        if (isim != null) {
349            return isim.getIsimIst();
350        } else {
351            return null;
352        }
353    }
354
355    public String[] getIsimPcscf() throws RemoteException {
356        Phone phone = getPhone(getDefaultSubscription());
357        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
358                "Requires READ_PRIVILEGED_PHONE_STATE");
359        IsimRecords isim = phone.getIsimRecords();
360        if (isim != null) {
361            return isim.getIsimPcscf();
362        } else {
363            return null;
364        }
365    }
366
367    public String getIsimChallengeResponse(String nonce) throws RemoteException {
368        Phone phone = getPhone(getDefaultSubscription());
369        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
370                "Requires READ_PRIVILEGED_PHONE_STATE");
371        IsimRecords isim = phone.getIsimRecords();
372        if (isim != null) {
373            return isim.getIsimChallengeResponse(nonce);
374        } else {
375            return null;
376        }
377    }
378
379    public String getIccSimChallengeResponse(int subId, int appType, int authType, String data)
380            throws RemoteException {
381        Phone phone = getPhone(subId);
382        enforcePrivilegedPermissionOrCarrierPrivilege(phone);
383        UiccCard uiccCard = phone.getUiccCard();
384        if (uiccCard == null) {
385            loge("getIccSimChallengeResponse() UiccCard is null");
386            return null;
387        }
388
389        UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType);
390        if (uiccApp == null) {
391            loge("getIccSimChallengeResponse() no app with specified type -- " +
392                    appType);
393            return null;
394        } else {
395            loge("getIccSimChallengeResponse() found app " + uiccApp.getAid()
396                    + " specified type -- " + appType);
397        }
398
399        if(authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM &&
400                authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA) {
401            loge("getIccSimChallengeResponse() unsupported authType: " + authType);
402            return null;
403        }
404
405        return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data);
406    }
407
408    public String getGroupIdLevel1(String callingPackage) {
409        return getGroupIdLevel1ForSubscriber(getDefaultSubscription(), callingPackage);
410    }
411
412    public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage) {
413        Phone phone = getPhone(subId);
414        if (phone != null) {
415            if (!checkReadPhoneState(callingPackage, "getGroupIdLevel1")) {
416                return null;
417            }
418            return phone.getGroupIdLevel1();
419        } else {
420            loge("getGroupIdLevel1 phone is null for Subscription:" + subId);
421            return null;
422        }
423    }
424
425    private boolean checkReadPhoneState(String callingPackage, String message) {
426        try {
427            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, message);
428
429            // SKIP checking run-time OP_READ_PHONE_STATE since self or using PRIVILEGED
430            return true;
431        } catch (SecurityException e) {
432            mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, message);
433        }
434
435        return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
436                callingPackage) == AppOpsManager.MODE_ALLOWED;
437    }
438
439    /**
440     * Besides READ_PHONE_STATE, WRITE_SMS and READ_SMS also allow apps to get phone numbers.
441     */
442    private boolean checkReadPhoneNumber(String callingPackage, String message) {
443        // Default SMS app can always read it.
444        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
445                Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
446            return true;
447        }
448        try {
449            return checkReadPhoneState(callingPackage, message);
450        } catch (SecurityException readPhoneStateSecurityException) {
451            try {
452                // Can be read with READ_SMS too.
453                mContext.enforceCallingOrSelfPermission(READ_SMS, message);
454                return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
455                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
456            } catch (SecurityException readSmsSecurityException) {
457                // Throw exception with message including both READ_PHONE_STATE and READ_SMS
458                // permissions
459                throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
460                        " nor current process has " + READ_PHONE_STATE + " or " + READ_SMS + ".");
461            }
462        }
463    }
464
465    private void log(String s) {
466        Rlog.d(TAG, s);
467    }
468
469    private void loge(String s) {
470        Rlog.e(TAG, s);
471    }
472}
473