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            return phone.getDeviceId();
74        } else {
75            loge("getDeviceIdForPhone phone " + phoneId + " is null");
76            return null;
77        }
78    }
79
80    public String getNaiForSubscriber(int subId, String callingPackage) {
81        Phone phone = getPhone(subId);
82        if (phone != null) {
83            if (!checkReadPhoneState(callingPackage, "getNai")) {
84                return null;
85            }
86            return phone.getNai();
87        } else {
88            loge("getNai phone is null for Subscription:" + subId);
89            return null;
90        }
91    }
92
93    public String getImeiForSubscriber(int subId, String callingPackage) {
94        Phone phone = getPhone(subId);
95        if (phone != null) {
96            if (!checkReadPhoneState(callingPackage, "getImei")) {
97                return null;
98            }
99            return phone.getImei();
100        } else {
101            loge("getDeviceId phone is null for Subscription:" + subId);
102            return null;
103        }
104    }
105
106    public String getDeviceSvn(String callingPackage) {
107        return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
108    }
109
110    public String getDeviceSvnUsingSubId(int subId, String callingPackage) {
111        Phone phone = getPhone(subId);
112        if (phone != null) {
113            if (!checkReadPhoneState(callingPackage, "getDeviceSvn")) {
114                return null;
115            }
116            return phone.getDeviceSvn();
117        } else {
118            loge("getDeviceSvn phone is null");
119            return null;
120        }
121    }
122
123    public String getSubscriberId(String callingPackage) {
124        return getSubscriberIdForSubscriber(getDefaultSubscription(), callingPackage);
125    }
126
127    public String getSubscriberIdForSubscriber(int subId, String callingPackage) {
128        Phone phone = getPhone(subId);
129        if (phone != null) {
130            if (!checkReadPhoneState(callingPackage, "getSubscriberId")) {
131                return null;
132            }
133            return phone.getSubscriberId();
134        } else {
135            loge("getSubscriberId phone is null for Subscription:" + subId);
136            return null;
137        }
138    }
139
140    /**
141     * Retrieves the serial number of the ICC, if applicable.
142     */
143    public String getIccSerialNumber(String callingPackage) {
144        return getIccSerialNumberForSubscriber(getDefaultSubscription(), callingPackage);
145    }
146
147    public String getIccSerialNumberForSubscriber(int subId, String callingPackage) {
148        Phone phone = getPhone(subId);
149        if (phone != null) {
150            if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {
151                return null;
152            }
153            return phone.getIccSerialNumber();
154        } else {
155            loge("getIccSerialNumber phone is null for Subscription:" + subId);
156            return null;
157        }
158    }
159
160    public String getLine1Number(String callingPackage) {
161        return getLine1NumberForSubscriber(getDefaultSubscription(), callingPackage);
162    }
163
164    public String getLine1NumberForSubscriber(int subId, String callingPackage) {
165        Phone phone = getPhone(subId);
166        if (phone != null) {
167            // This is open to apps with WRITE_SMS.
168            if (!checkReadPhoneNumber(callingPackage, "getLine1Number")) {
169                return null;
170            }
171            return phone.getLine1Number();
172        } else {
173            loge("getLine1Number phone is null for Subscription:" + subId);
174            return null;
175        }
176    }
177
178    public String getLine1AlphaTag(String callingPackage) {
179        return getLine1AlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
180    }
181
182    public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) {
183        Phone phone = getPhone(subId);
184        if (phone != null) {
185            if (!checkReadPhoneState(callingPackage, "getLine1AlphaTag")) {
186                return null;
187            }
188            return phone.getLine1AlphaTag();
189        } else {
190            loge("getLine1AlphaTag phone is null for Subscription:" + subId);
191            return null;
192        }
193    }
194
195    public String getMsisdn(String callingPackage) {
196        return getMsisdnForSubscriber(getDefaultSubscription(), callingPackage);
197    }
198
199    public String getMsisdnForSubscriber(int subId, String callingPackage) {
200        Phone phone = getPhone(subId);
201        if (phone != null) {
202            if (!checkReadPhoneState(callingPackage, "getMsisdn")) {
203                return null;
204            }
205            return phone.getMsisdn();
206        } else {
207            loge("getMsisdn phone is null for Subscription:" + subId);
208            return null;
209        }
210    }
211
212    public String getVoiceMailNumber(String callingPackage) {
213        return getVoiceMailNumberForSubscriber(getDefaultSubscription(), callingPackage);
214    }
215
216    public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) {
217        Phone phone = getPhone(subId);
218        if (phone != null) {
219            if (!checkReadPhoneState(callingPackage, "getVoiceMailNumber")) {
220                return null;
221            }
222            String number = PhoneNumberUtils.extractNetworkPortion(phone.getVoiceMailNumber());
223            if (VDBG) log("VM: getVoiceMailNUmber: " + number);
224            return number;
225        } else {
226            loge("getVoiceMailNumber phone is null for Subscription:" + subId);
227            return null;
228        }
229    }
230
231    // TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
232    public String getCompleteVoiceMailNumber() {
233        return getCompleteVoiceMailNumberForSubscriber(getDefaultSubscription());
234    }
235
236    public String getCompleteVoiceMailNumberForSubscriber(int subId) {
237        Phone phone = getPhone(subId);
238        if (phone != null) {
239            mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED, "Requires CALL_PRIVILEGED");
240            String number = phone.getVoiceMailNumber();
241            if (VDBG) log("VM: getCompleteVoiceMailNUmber: " + number);
242            return number;
243        } else {
244            loge("getCompleteVoiceMailNumber phone is null for Subscription:" + subId);
245            return null;
246        }
247    }
248
249    public String getVoiceMailAlphaTag(String callingPackage) {
250        return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
251    }
252
253    public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) {
254        Phone phone = getPhone(subId);
255        if (phone != null) {
256            if (!checkReadPhoneState(callingPackage, "getVoiceMailAlphaTag")) {
257                return null;
258            }
259            return phone.getVoiceMailAlphaTag();
260        } else {
261            loge("getVoiceMailAlphaTag phone is null for Subscription:" + subId);
262            return null;
263        }
264    }
265
266    /**
267     * get Phone object based on subId.
268     **/
269    private Phone getPhone(int subId) {
270        int phoneId = SubscriptionManager.getPhoneId(subId);
271        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
272            phoneId = 0;
273        }
274        return mPhone[phoneId];
275    }
276
277    /**
278     * Make sure caller has either read privileged phone permission or carrier privilege.
279     *
280     * @throws SecurityException if the caller does not have the required permission/privilege
281     */
282    private void enforcePrivilegedPermissionOrCarrierPrivilege(Phone phone) {
283        int permissionResult = mContext.checkCallingOrSelfPermission(
284                READ_PRIVILEGED_PHONE_STATE);
285        if (permissionResult == PackageManager.PERMISSION_GRANTED) {
286            return;
287        }
288        log("No read privileged phone permission, check carrier privilege next.");
289        UiccCard uiccCard = phone.getUiccCard();
290        if (uiccCard == null) {
291            throw new SecurityException("No Carrier Privilege: No UICC");
292        }
293        if (uiccCard.getCarrierPrivilegeStatusForCurrentTransaction(
294                mContext.getPackageManager()) != CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
295            throw new SecurityException("No Carrier Privilege.");
296        }
297    }
298
299    private int getDefaultSubscription() {
300        return  PhoneFactory.getDefaultSubscription();
301    }
302
303
304    public String getIsimImpi() {
305        Phone phone = getPhone(getDefaultSubscription());
306        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
307                "Requires READ_PRIVILEGED_PHONE_STATE");
308        IsimRecords isim = phone.getIsimRecords();
309        if (isim != null) {
310            return isim.getIsimImpi();
311        } else {
312            return null;
313        }
314    }
315
316    public String getIsimDomain() {
317        Phone phone = getPhone(getDefaultSubscription());
318        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
319                "Requires READ_PRIVILEGED_PHONE_STATE");
320        IsimRecords isim = phone.getIsimRecords();
321        if (isim != null) {
322            return isim.getIsimDomain();
323        } else {
324            return null;
325        }
326    }
327
328    public String[] getIsimImpu() {
329        Phone phone = getPhone(getDefaultSubscription());
330        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
331                "Requires READ_PRIVILEGED_PHONE_STATE");
332        IsimRecords isim = phone.getIsimRecords();
333        if (isim != null) {
334            return isim.getIsimImpu();
335        } else {
336            return null;
337        }
338    }
339
340    public String getIsimIst() throws RemoteException {
341        Phone phone = getPhone(getDefaultSubscription());
342        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
343                "Requires READ_PRIVILEGED_PHONE_STATE");
344        IsimRecords isim = phone.getIsimRecords();
345        if (isim != null) {
346            return isim.getIsimIst();
347        } else {
348            return null;
349        }
350    }
351
352    public String[] getIsimPcscf() throws RemoteException {
353        Phone phone = getPhone(getDefaultSubscription());
354        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
355                "Requires READ_PRIVILEGED_PHONE_STATE");
356        IsimRecords isim = phone.getIsimRecords();
357        if (isim != null) {
358            return isim.getIsimPcscf();
359        } else {
360            return null;
361        }
362    }
363
364    public String getIsimChallengeResponse(String nonce) throws RemoteException {
365        Phone phone = getPhone(getDefaultSubscription());
366        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
367                "Requires READ_PRIVILEGED_PHONE_STATE");
368        IsimRecords isim = phone.getIsimRecords();
369        if (isim != null) {
370            return isim.getIsimChallengeResponse(nonce);
371        } else {
372            return null;
373        }
374    }
375
376    public String getIccSimChallengeResponse(int subId, int appType, int authType, String data)
377            throws RemoteException {
378        Phone phone = getPhone(subId);
379        enforcePrivilegedPermissionOrCarrierPrivilege(phone);
380        UiccCard uiccCard = phone.getUiccCard();
381        if (uiccCard == null) {
382            loge("getIccSimChallengeResponse() UiccCard is null");
383            return null;
384        }
385
386        UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType);
387        if (uiccApp == null) {
388            loge("getIccSimChallengeResponse() no app with specified type -- " +
389                    appType);
390            return null;
391        } else {
392            loge("getIccSimChallengeResponse() found app " + uiccApp.getAid()
393                    + " specified type -- " + appType);
394        }
395
396        if(authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM &&
397                authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA) {
398            loge("getIccSimChallengeResponse() unsupported authType: " + authType);
399            return null;
400        }
401
402        return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data);
403    }
404
405    public String getGroupIdLevel1(String callingPackage) {
406        return getGroupIdLevel1ForSubscriber(getDefaultSubscription(), callingPackage);
407    }
408
409    public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage) {
410        Phone phone = getPhone(subId);
411        if (phone != null) {
412            if (!checkReadPhoneState(callingPackage, "getGroupIdLevel1")) {
413                return null;
414            }
415            return phone.getGroupIdLevel1();
416        } else {
417            loge("getGroupIdLevel1 phone is null for Subscription:" + subId);
418            return null;
419        }
420    }
421
422    private boolean checkReadPhoneState(String callingPackage, String message) {
423        try {
424            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, message);
425
426            // SKIP checking run-time OP_READ_PHONE_STATE since self or using PRIVILEGED
427            return true;
428        } catch (SecurityException e) {
429            mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, message);
430        }
431
432        return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
433                callingPackage) == AppOpsManager.MODE_ALLOWED;
434    }
435
436    /**
437     * Besides READ_PHONE_STATE, WRITE_SMS and READ_SMS also allow apps to get phone numbers.
438     */
439    private boolean checkReadPhoneNumber(String callingPackage, String message) {
440        // Default SMS app can always read it.
441        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
442                Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
443            return true;
444        }
445        try {
446            return checkReadPhoneState(callingPackage, message);
447        } catch (SecurityException readPhoneStateSecurityException) {
448            try {
449                // Can be read with READ_SMS too.
450                mContext.enforceCallingOrSelfPermission(READ_SMS, message);
451                return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
452                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
453            } catch (SecurityException readSmsSecurityException) {
454                // Throw exception with message including both READ_PHONE_STATE and READ_SMS
455                // permissions
456                throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
457                        " nor current process has " + READ_PHONE_STATE + " or " + READ_SMS + ".");
458            }
459        }
460    }
461
462    private void log(String s) {
463        Rlog.d(TAG, s);
464    }
465
466    private void loge(String s) {
467        Rlog.e(TAG, s);
468    }
469}
470