/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.telephony.CellInfo; import android.telephony.CellLocation; import android.telephony.DisconnectCause; import android.telephony.PhoneStateListener; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.PreciseDisconnectCause; import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.VoLteServiceState; import android.text.TextUtils; import android.util.LocalLog; import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstantConversions; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.am.BatteryStatsService; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Since phone process can be restarted, this class provides a centralized place * that applications can register and be called back from. * * Change-Id: I450c968bda93767554b5188ee63e10c9f43c5aa4 fixes bugs 16148026 * and 15973975 by saving the phoneId of the registrant and then using the * phoneId when deciding to to make a callback. This is necessary because * a subId changes from to a dummy value when a SIM is removed and thus won't * compare properly. Because SubscriptionManager.getPhoneId(int subId) handles * the dummy value conversion we properly do the callbacks. * * Eventually we may want to remove the notion of dummy value but for now this * looks like the best approach. */ class TelephonyRegistry extends ITelephonyRegistry.Stub { private static final String TAG = "TelephonyRegistry"; private static final boolean DBG = false; // STOPSHIP if true private static final boolean DBG_LOC = false; // STOPSHIP if true private static final boolean VDBG = false; // STOPSHIP if true private static class Record { String callingPackage; IBinder binder; IPhoneStateListener callback; IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback; int callerUserId; int events; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; int phoneId = SubscriptionManager.INVALID_PHONE_INDEX; boolean canReadPhoneState; boolean matchPhoneStateListenerEvent(int events) { return (callback != null) && ((events & this.events) != 0); } boolean matchOnSubscriptionsChangedListener() { return (onSubscriptionsChangedListenerCallback != null); } @Override public String toString() { return "{callingPackage=" + callingPackage + " binder=" + binder + " callback=" + callback + " onSubscriptionsChangedListenererCallback=" + onSubscriptionsChangedListenerCallback + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + " canReadPhoneState=" + canReadPhoneState + "}"; } } private final Context mContext; // access should be inside synchronized (mRecords) for these two fields private final ArrayList mRemoveList = new ArrayList(); private final ArrayList mRecords = new ArrayList(); private final IBatteryStats mBatteryStats; private final AppOpsManager mAppOps; private boolean hasNotifySubscriptionInfoChangedOccurred = false; private int mNumPhones; private int[] mCallState; private String[] mCallIncomingNumber; private ServiceState[] mServiceState; private int[] mVoiceActivationState; private int[] mDataActivationState; private SignalStrength[] mSignalStrength; private boolean[] mMessageWaiting; private boolean[] mCallForwarding; private int[] mDataActivity; private int[] mDataConnectionState; private ArrayList[] mConnectedApns; private LinkProperties[] mDataConnectionLinkProperties; private NetworkCapabilities[] mDataConnectionNetworkCapabilities; private Bundle[] mCellLocation; private int[] mDataConnectionNetworkType; private int mOtaspMode = TelephonyManager.OTASP_UNKNOWN; private ArrayList> mCellInfo = null; private VoLteServiceState mVoLteServiceState = new VoLteServiceState(); private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private int mDefaultPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; private int mRingingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; private int mForegroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; private int mBackgroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE; private PreciseCallState mPreciseCallState = new PreciseCallState(); private boolean mCarrierNetworkChangeState = false; private final LocalLog mLocalLog = new LocalLog(100); private PreciseDataConnectionState mPreciseDataConnectionState = new PreciseDataConnectionState(); static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR | PhoneStateListener.LISTEN_VOLTE_STATE; static final int CHECK_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_DATA_ACTIVITY | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE; static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE; private static final int MSG_USER_SWITCHED = 1; private static final int MSG_UPDATE_DEFAULT_SUB = 2; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_USER_SWITCHED: { if (VDBG) log("MSG_USER_SWITCHED userId=" + msg.arg1); int numPhones = TelephonyManager.getDefault().getPhoneCount(); for (int sub = 0; sub < numPhones; sub++) { TelephonyRegistry.this.notifyCellLocationForSubscriber(sub, mCellLocation[sub]); } break; } case MSG_UPDATE_DEFAULT_SUB: { int newDefaultPhoneId = msg.arg1; int newDefaultSubId = (Integer)(msg.obj); if (VDBG) { log("MSG_UPDATE_DEFAULT_SUB:current mDefaultSubId=" + mDefaultSubId + " current mDefaultPhoneId=" + mDefaultPhoneId + " newDefaultSubId= " + newDefaultSubId + " newDefaultPhoneId=" + newDefaultPhoneId); } //Due to possible risk condition,(notify call back using the new //defaultSubId comes before new defaultSubId update) we need to recall all //possible missed notify callback synchronized (mRecords) { for (Record r : mRecords) { if(r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { checkPossibleMissNotify(r, newDefaultPhoneId); } } handleRemoveListLocked(); } mDefaultSubId = newDefaultSubId; mDefaultPhoneId = newDefaultPhoneId; } } } }; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (VDBG) log("mBroadcastReceiver: action=" + action); if (Intent.ACTION_USER_SWITCHED.equals(action)) { int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); if (DBG) log("onReceive: userHandle=" + userHandle); mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0)); } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) { Integer newDefaultSubIdObj = new Integer(intent.getIntExtra( PhoneConstants.SUBSCRIPTION_KEY, SubscriptionManager.getDefaultSubscriptionId())); int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.SLOT_KEY, SubscriptionManager.getPhoneId(mDefaultSubId)); if (DBG) { log("onReceive:current mDefaultSubId=" + mDefaultSubId + " current mDefaultPhoneId=" + mDefaultPhoneId + " newDefaultSubId= " + newDefaultSubIdObj + " newDefaultPhoneId=" + newDefaultPhoneId); } if(validatePhoneId(newDefaultPhoneId) && (!newDefaultSubIdObj.equals(mDefaultSubId) || (newDefaultPhoneId != mDefaultPhoneId))) { mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB, newDefaultPhoneId, 0, newDefaultSubIdObj)); } } } }; // we keep a copy of all of the state so we can send it out when folks // register for it // // In these calls we call with the lock held. This is safe becasuse remote // calls go through a oneway interface and local calls going through a // handler before they get to app code. TelephonyRegistry(Context context) { CellLocation location = CellLocation.getEmpty(); mContext = context; mBatteryStats = BatteryStatsService.getService(); int numPhones = TelephonyManager.getDefault().getPhoneCount(); if (DBG) log("TelephonyRegistor: ctor numPhones=" + numPhones); mNumPhones = numPhones; mConnectedApns = new ArrayList[numPhones]; mCallState = new int[numPhones]; mDataActivity = new int[numPhones]; mDataConnectionState = new int[numPhones]; mDataConnectionNetworkType = new int[numPhones]; mCallIncomingNumber = new String[numPhones]; mServiceState = new ServiceState[numPhones]; mVoiceActivationState = new int[numPhones]; mDataActivationState = new int[numPhones]; mSignalStrength = new SignalStrength[numPhones]; mMessageWaiting = new boolean[numPhones]; mCallForwarding = new boolean[numPhones]; mCellLocation = new Bundle[numPhones]; mDataConnectionLinkProperties = new LinkProperties[numPhones]; mDataConnectionNetworkCapabilities = new NetworkCapabilities[numPhones]; mCellInfo = new ArrayList>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; mDataConnectionState[i] = TelephonyManager.DATA_UNKNOWN; mVoiceActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; mCallIncomingNumber[i] = ""; mServiceState[i] = new ServiceState(); mSignalStrength[i] = new SignalStrength(); mMessageWaiting[i] = false; mCallForwarding[i] = false; mCellLocation[i] = new Bundle(); mCellInfo.add(i, null); mConnectedApns[i] = new ArrayList(); } // Note that location can be null for non-phone builds like // like the generic one. if (location != null) { for (int i = 0; i < numPhones; i++) { location.fillInNotifierBundle(mCellLocation[i]); } } mAppOps = mContext.getSystemService(AppOpsManager.class); } public void systemRunning() { // Watch for interesting updates final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); log("systemRunning register for intents"); mContext.registerReceiver(mBroadcastReceiver, filter); } @Override public void addOnSubscriptionsChangedListener(String callingPackage, IOnSubscriptionsChangedListener callback) { int callerUserId = UserHandle.getCallingUserId(); if (VDBG) { log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId + " callback=" + callback + " callback.asBinder=" + callback.asBinder()); } try { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, "addOnSubscriptionsChangedListener"); // SKIP checking for run-time permission since caller or self has PRIVILEGED permission } catch (SecurityException e) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PHONE_STATE, "addOnSubscriptionsChangedListener"); if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } } Record r; synchronized (mRecords) { // register find_and_add: { IBinder b = callback.asBinder(); final int N = mRecords.size(); for (int i = 0; i < N; i++) { r = mRecords.get(i); if (b == r.binder) { break find_and_add; } } r = new Record(); r.binder = b; mRecords.add(r); if (DBG) log("listen oscl: add new record"); } r.onSubscriptionsChangedListenerCallback = callback; r.callingPackage = callingPackage; r.callerUserId = callerUserId; r.events = 0; r.canReadPhoneState = true; // permission has been enforced above if (DBG) { log("listen oscl: Register r=" + r); } // Always notify when registration occurs if there has been a notification. if (hasNotifySubscriptionInfoChangedOccurred) { try { if (VDBG) log("listen oscl: send to r=" + r); r.onSubscriptionsChangedListenerCallback.onSubscriptionsChanged(); if (VDBG) log("listen oscl: sent to r=" + r); } catch (RemoteException e) { if (VDBG) log("listen oscl: remote exception sending to r=" + r + " e=" + e); remove(r.binder); } } else { log("listen oscl: hasNotifySubscriptionInfoChangedOccurred==false no callback"); } } } @Override public void removeOnSubscriptionsChangedListener(String pkgForDebug, IOnSubscriptionsChangedListener callback) { if (DBG) log("listen oscl: Unregister"); remove(callback.asBinder()); } @Override public void notifySubscriptionInfoChanged() { if (VDBG) log("notifySubscriptionInfoChanged:"); synchronized (mRecords) { if (!hasNotifySubscriptionInfoChangedOccurred) { log("notifySubscriptionInfoChanged: first invocation mRecords.size=" + mRecords.size()); } hasNotifySubscriptionInfoChangedOccurred = true; mRemoveList.clear(); for (Record r : mRecords) { if (r.matchOnSubscriptionsChangedListener()) { try { if (VDBG) log("notifySubscriptionInfoChanged: call osc to r=" + r); r.onSubscriptionsChangedListenerCallback.onSubscriptionsChanged(); if (VDBG) log("notifySubscriptionInfoChanged: done osc to r=" + r); } catch (RemoteException ex) { if (VDBG) log("notifySubscriptionInfoChanged: RemoteException r=" + r); mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } } @Override public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, pkgForDebug, callback, events, notifyNow); } @Override public void listenForSubscriber(int subId, String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { listen(pkgForDebug, callback, events, notifyNow, subId); } private void listen(String callingPackage, IPhoneStateListener callback, int events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); if (VDBG) { log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId); } if (events != PhoneStateListener.LISTEN_NONE) { /* Checks permission and throws Security exception */ checkListenerPermission(events); if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) { try { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); // SKIP checking for run-time permission since caller or self has PRIVILEGED // permission } catch (SecurityException e) { if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } } } synchronized (mRecords) { // register Record r; find_and_add: { IBinder b = callback.asBinder(); final int N = mRecords.size(); for (int i = 0; i < N; i++) { r = mRecords.get(i); if (b == r.binder) { break find_and_add; } } r = new Record(); r.binder = b; mRecords.add(r); if (DBG) log("listen: add new record"); } r.callback = callback; r.callingPackage = callingPackage; r.callerUserId = callerUserId; boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0; r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage); // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID if (!SubscriptionManager.isValidSubscriptionId(subId)) { r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; } else {//APP specify subID r.subId = subId; } r.phoneId = SubscriptionManager.getPhoneId(r.subId); int phoneId = r.phoneId; r.events = events; if (DBG) { log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); } if (notifyNow && validatePhoneId(phoneId)) { if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); r.callback.onServiceStateChanged( new ServiceState(mServiceState[phoneId])); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { int gsmSignalStrength = mSignalStrength[phoneId] .getGsmSignalStrength(); r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 : gsmSignalStrength)); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { try { r.callback.onMessageWaitingIndicatorChanged( mMessageWaiting[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { try { r.callback.onCallForwardingIndicatorChanged( mCallForwarding[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { if (DBG_LOC) log("listen: mCellLocation = " + mCellLocation[phoneId]); r.callback.onCellLocationChanged( new Bundle(mCellLocation[phoneId])); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { try { r.callback.onCallStateChanged(mCallState[phoneId], getCallIncomingNumber(r, phoneId)); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { try { r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { try { r.callback.onDataActivity(mDataActivity[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { try { r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) { try { r.callback.onOtaspChanged(mOtaspMode); } catch (RemoteException ex) { remove(r.binder); } } if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { try { r.callback.onPreciseDataConnectionStateChanged( mPreciseDataConnectionState); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { try { r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) { try { r.callback.onVoiceActivationStateChanged(mVoiceActivationState[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) { try { r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } } } } else { if(DBG) log("listen: Unregister"); remove(callback.asBinder()); } } private boolean canReadPhoneState(String callingPackage) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // SKIP checking for run-time permission since caller or self has PRIVILEGED permission return true; } boolean canReadPhoneState = mContext.checkCallingOrSelfPermission( android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; if (canReadPhoneState && mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return false; } return canReadPhoneState; } private String getCallIncomingNumber(Record record, int phoneId) { // Hide the number if record's process has no READ_PHONE_STATE permission return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : ""; } private void remove(IBinder binder) { synchronized (mRecords) { final int recordCount = mRecords.size(); for (int i = 0; i < recordCount; i++) { if (mRecords.get(i).binder == binder) { if (DBG) { Record r = mRecords.get(i); log("remove: binder=" + binder + "r.callingPackage" + r.callingPackage + "r.callback" + r.callback); } mRecords.remove(i); return; } } } } public void notifyCallState(int state, String incomingNumber) { if (!checkNotifyPermission("notifyCallState()")) { return; } if (VDBG) { log("notifyCallState: state=" + state + " incomingNumber=" + incomingNumber); } synchronized (mRecords) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { String incomingNumberOrEmpty = r.canReadPhoneState ? incomingNumber : ""; r.callback.onCallStateChanged(state, incomingNumberOrEmpty); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } // Called only by Telecomm to communicate call state across different phone accounts. So // there is no need to add a valid subId or slotId. broadcastCallStateChanged(state, incomingNumber, SubscriptionManager.INVALID_PHONE_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } public void notifyCallStateForPhoneId(int phoneId, int subId, int state, String incomingNumber) { if (!checkNotifyPermission("notifyCallState()")) { return; } if (VDBG) { log("notifyCallStateForPhoneId: subId=" + subId + " state=" + state + " incomingNumber=" + incomingNumber); } synchronized (mRecords) { if (validatePhoneId(phoneId)) { mCallState[phoneId] = state; mCallIncomingNumber[phoneId] = incomingNumber; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && (r.subId == subId) && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId); r.callback.onCallStateChanged(state, incomingNumberOrEmpty); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } broadcastCallStateChanged(state, incomingNumber, phoneId, subId); } public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) { if (!checkNotifyPermission("notifyServiceState()")){ return; } synchronized (mRecords) { String str = "notifyServiceStateForSubscriber: subId=" + subId + " phoneId=" + phoneId + " state=" + state; if (VDBG) { log(str); } mLocalLog.log(str); if (validatePhoneId(phoneId)) { mServiceState[phoneId] = state; for (Record r : mRecords) { if (VDBG) { log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { log("notifyServiceStateForSubscriber: callback.onSSC r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } r.callback.onServiceStateChanged(new ServiceState(state)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } else { log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId); } handleRemoveListLocked(); } broadcastServiceStateChanged(state, phoneId, subId); } public void notifySimActivationStateChangedForPhoneId(int phoneId, int subId, int activationType, int activationState) { if (!checkNotifyPermission("notifySimActivationState()")){ return; } if (VDBG) { log("notifySimActivationStateForPhoneId: subId=" + subId + " phoneId=" + phoneId + "type=" + activationType + " state=" + activationState); } synchronized (mRecords) { if (validatePhoneId(phoneId)) { switch (activationType) { case PhoneConstants.SIM_ACTIVATION_TYPE_VOICE: mVoiceActivationState[phoneId] = activationState; break; case PhoneConstants.SIM_ACTIVATION_TYPE_DATA: mDataActivationState[phoneId] = activationState; break; default: return; } for (Record r : mRecords) { if (VDBG) { log("notifySimActivationStateForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + "type=" + activationType + " state=" + activationState); } try { if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_VOICE) && r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + activationState); } r.callback.onVoiceActivationStateChanged(activationState); } if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_DATA) && r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + activationState); } r.callback.onDataActivationStateChanged(activationState); } } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } else { log("notifySimActivationStateForPhoneId: INVALID phoneId=" + phoneId); } handleRemoveListLocked(); } } public void notifySignalStrengthForPhoneId(int phoneId, int subId, SignalStrength signalStrength) { if (!checkNotifyPermission("notifySignalStrength()")) { return; } if (VDBG) { log("notifySignalStrengthForPhoneId: subId=" + subId +" phoneId=" + phoneId + " signalStrength=" + signalStrength); } synchronized (mRecords) { if (validatePhoneId(phoneId)) { if (VDBG) log("notifySignalStrengthForPhoneId: valid phoneId=" + phoneId); mSignalStrength[phoneId] = signalStrength; for (Record r : mRecords) { if (VDBG) { log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) && idMatch(r.subId, subId, phoneId)){ try { int gsmSignalStrength = signalStrength.getGsmSignalStrength(); int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); if (DBG) { log("notifySignalStrengthForPhoneId: callback.onSS r=" + r + " subId=" + subId + " phoneId=" + phoneId + " gsmSS=" + gsmSignalStrength + " ss=" + ss); } r.callback.onSignalStrengthChanged(ss); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } else { log("notifySignalStrengthForPhoneId: invalid phoneId=" + phoneId); } handleRemoveListLocked(); } broadcastSignalStrengthChanged(signalStrength, phoneId, subId); } @Override public void notifyCarrierNetworkChange(boolean active) { enforceNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()"); if (VDBG) { log("notifyCarrierNetworkChange: active=" + active); } synchronized (mRecords) { mCarrierNetworkChangeState = active; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE)) { try { r.callback.onCarrierNetworkChange(active); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } } public void notifyCellInfo(List cellInfo) { notifyCellInfoForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellInfo); } public void notifyCellInfoForSubscriber(int subId, List cellInfo) { if (!checkNotifyPermission("notifyCellInfo()")) { return; } if (VDBG) { log("notifyCellInfoForSubscriber: subId=" + subId + " cellInfo=" + cellInfo); } synchronized (mRecords) { int phoneId = SubscriptionManager.getPhoneId(subId); if (validatePhoneId(phoneId)) { mCellInfo.set(phoneId, cellInfo); for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r); } r.callback.onCellInfoChanged(cellInfo); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } } @Override public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) { if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { return; } if (VDBG) { log("notifyMessageWaitingChangedForSubscriberPhoneID: subId=" + phoneId + " mwi=" + mwi); } synchronized (mRecords) { if (validatePhoneId(phoneId)) { mMessageWaiting[phoneId] = mwi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onMessageWaitingIndicatorChanged(mwi); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } } public void notifyCallForwardingChanged(boolean cfi) { notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi); } public void notifyCallForwardingChangedForSubscriber(int subId, boolean cfi) { if (!checkNotifyPermission("notifyCallForwardingChanged()")) { return; } if (VDBG) { log("notifyCallForwardingChangedForSubscriber: subId=" + subId + " cfi=" + cfi); } synchronized (mRecords) { int phoneId = SubscriptionManager.getPhoneId(subId); if (validatePhoneId(phoneId)) { mCallForwarding[phoneId] = cfi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallForwardingIndicatorChanged(cfi); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } } public void notifyDataActivity(int state) { notifyDataActivityForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state); } public void notifyDataActivityForSubscriber(int subId, int state) { if (!checkNotifyPermission("notifyDataActivity()" )) { return; } synchronized (mRecords) { int phoneId = SubscriptionManager.getPhoneId(subId); if (validatePhoneId(phoneId)) { mDataActivity[phoneId] = state; for (Record r : mRecords) { // Notify by correct subId. if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onDataActivity(state); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } } public void notifyDataConnection(int state, boolean isDataAllowed, String reason, String apn, String apnType, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int networkType, boolean roaming) { notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state, isDataAllowed,reason, apn, apnType, linkProperties, networkCapabilities, networkType, roaming); } public void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataAllowed, String reason, String apn, String apnType, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int networkType, boolean roaming) { if (!checkNotifyPermission("notifyDataConnection()" )) { return; } if (VDBG) { log("notifyDataConnectionForSubscriber: subId=" + subId + " state=" + state + " isDataAllowed=" + isDataAllowed + " reason='" + reason + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType + " mRecords.size()=" + mRecords.size()); } synchronized (mRecords) { int phoneId = SubscriptionManager.getPhoneId(subId); if (validatePhoneId(phoneId)) { boolean modified = false; if (state == TelephonyManager.DATA_CONNECTED) { if (!mConnectedApns[phoneId].contains(apnType)) { mConnectedApns[phoneId].add(apnType); if (mDataConnectionState[phoneId] != state) { mDataConnectionState[phoneId] = state; modified = true; } } } else { if (mConnectedApns[phoneId].remove(apnType)) { if (mConnectedApns[phoneId].isEmpty()) { mDataConnectionState[phoneId] = state; modified = true; } else { // leave mDataConnectionState as is and // send out the new status for the APN in question. } } } mDataConnectionLinkProperties[phoneId] = linkProperties; mDataConnectionNetworkCapabilities[phoneId] = networkCapabilities; if (mDataConnectionNetworkType[phoneId] != networkType) { mDataConnectionNetworkType[phoneId] = networkType; // need to tell registered listeners about the new network type modified = true; } if (modified) { String str = "onDataConnectionStateChanged(" + mDataConnectionState[phoneId] + ", " + mDataConnectionNetworkType[phoneId] + ")"; log(str); mLocalLog.log(str); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { log("Notify data connection state changed on sub: " + subId); } r.callback.onDataConnectionStateChanged( mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType, apnType, apn, reason, linkProperties, ""); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) { try { r.callback.onPreciseDataConnectionStateChanged( mPreciseDataConnectionState); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } broadcastDataConnectionStateChanged(state, isDataAllowed, reason, apn, apnType, linkProperties, networkCapabilities, roaming, subId); broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason, linkProperties, ""); } public void notifyDataConnectionFailed(String reason, String apnType) { notifyDataConnectionFailedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, reason, apnType); } public void notifyDataConnectionFailedForSubscriber(int subId, String reason, String apnType) { if (!checkNotifyPermission("notifyDataConnectionFailed()")) { return; } if (VDBG) { log("notifyDataConnectionFailedForSubscriber: subId=" + subId + " reason=" + reason + " apnType=" + apnType); } synchronized (mRecords) { mPreciseDataConnectionState = new PreciseDataConnectionState( TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, ""); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) { try { r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } broadcastDataConnectionFailed(reason, apnType, subId); broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, ""); } public void notifyCellLocation(Bundle cellLocation) { notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellLocation); } public void notifyCellLocationForSubscriber(int subId, Bundle cellLocation) { log("notifyCellLocationForSubscriber: subId=" + subId + " cellLocation=" + cellLocation); if (!checkNotifyPermission("notifyCellLocation()")) { return; } if (VDBG) { log("notifyCellLocationForSubscriber: subId=" + subId + " cellLocation=" + cellLocation); } synchronized (mRecords) { int phoneId = SubscriptionManager.getPhoneId(subId); if (validatePhoneId(phoneId)) { mCellLocation[phoneId] = cellLocation; for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation + " r=" + r); } r.callback.onCellLocationChanged(new Bundle(cellLocation)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } } handleRemoveListLocked(); } } public void notifyOtaspChanged(int otaspMode) { if (!checkNotifyPermission("notifyOtaspChanged()" )) { return; } synchronized (mRecords) { mOtaspMode = otaspMode; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_OTASP_CHANGED)) { try { r.callback.onOtaspChanged(otaspMode); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } } public void notifyPreciseCallState(int ringingCallState, int foregroundCallState, int backgroundCallState) { if (!checkNotifyPermission("notifyPreciseCallState()")) { return; } synchronized (mRecords) { mRingingCallState = ringingCallState; mForegroundCallState = foregroundCallState; mBackgroundCallState = backgroundCallState; mPreciseCallState = new PreciseCallState(ringingCallState, foregroundCallState, backgroundCallState, DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState, DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID); } public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) { if (!checkNotifyPermission("notifyDisconnectCause()")) { return; } synchronized (mRecords) { mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState, mBackgroundCallState, disconnectCause, preciseDisconnectCause); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState, mBackgroundCallState, disconnectCause, preciseDisconnectCause); } public void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn, String failCause) { if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) { return; } synchronized (mRecords) { mPreciseDataConnectionState = new PreciseDataConnectionState( TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, reason, null, failCause); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) { try { r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, reason, null, failCause); } public void notifyVoLteServiceStateChanged(VoLteServiceState lteState) { if (!checkNotifyPermission("notifyVoLteServiceStateChanged()")) { return; } synchronized (mRecords) { mVoLteServiceState = lteState; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_VOLTE_STATE)) { try { r.callback.onVoLteServiceStateChanged( new VoLteServiceState(mVoLteServiceState)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } } public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) { if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) { return; } synchronized (mRecords) { for (Record r : mRecords) { if (VDBG) { log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId); } if ((r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) && ((r.subId == subId) || (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) { try { r.callback.onOemHookRawEvent(rawData); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } handleRemoveListLocked(); } } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; synchronized (mRecords) { final int recordCount = mRecords.size(); pw.println("last known state:"); pw.increaseIndent(); for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { pw.println("Phone Id=" + i); pw.increaseIndent(); pw.println("mCallState=" + mCallState[i]); pw.println("mCallIncomingNumber=" + mCallIncomingNumber[i]); pw.println("mServiceState=" + mServiceState[i]); pw.println("mVoiceActivationState= " + mVoiceActivationState[i]); pw.println("mDataActivationState= " + mDataActivationState[i]); pw.println("mSignalStrength=" + mSignalStrength[i]); pw.println("mMessageWaiting=" + mMessageWaiting[i]); pw.println("mCallForwarding=" + mCallForwarding[i]); pw.println("mDataActivity=" + mDataActivity[i]); pw.println("mDataConnectionState=" + mDataConnectionState[i]); pw.println("mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]); pw.println("mDataConnectionNetworkCapabilities=" + mDataConnectionNetworkCapabilities[i]); pw.println("mCellLocation=" + mCellLocation[i]); pw.println("mCellInfo=" + mCellInfo.get(i)); pw.decreaseIndent(); } pw.println("mConnectedApns=" + Arrays.toString(mConnectedApns)); pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState); pw.println("mPreciseCallState=" + mPreciseCallState); pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); pw.println("mRingingCallState=" + mRingingCallState); pw.println("mForegroundCallState=" + mForegroundCallState); pw.println("mBackgroundCallState=" + mBackgroundCallState); pw.println("mVoLteServiceState=" + mVoLteServiceState); pw.decreaseIndent(); pw.println("local logs:"); pw.increaseIndent(); mLocalLog.dump(fd, pw, args); pw.decreaseIndent(); pw.println("registrations: count=" + recordCount); pw.increaseIndent(); for (Record r : mRecords) { pw.println(r); } pw.decreaseIndent(); } } // // the legacy intent broadcasting // private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) { long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneState(state.getState()); } catch (RemoteException re) { // Can't do much } finally { Binder.restoreCallingIdentity(ident); } Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); Bundle data = new Bundle(); state.fillInNotifierBundle(data); intent.putExtras(data); // Pass the subscription along with the intent. intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId, int subId) { long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneSignalStrength(signalStrength); } catch (RemoteException e) { /* The remote entity disappeared, we can safely ignore the exception. */ } finally { Binder.restoreCallingIdentity(ident); } Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); Bundle data = new Bundle(); signalStrength.fillInNotifierBundle(data); intent.putExtras(data); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } /** * Broadcasts an intent notifying apps of a phone state change. {@code subId} can be * a valid subId, in which case this function fires a subId-specific intent, or it * can be {@code SubscriptionManager.INVALID_SUBSCRIPTION_ID}, in which case we send * a global state change broadcast ({@code TelephonyManager.ACTION_PHONE_STATE_CHANGED}). */ private void broadcastCallStateChanged(int state, String incomingNumber, int phoneId, int subId) { long ident = Binder.clearCallingIdentity(); try { if (state == TelephonyManager.CALL_STATE_IDLE) { mBatteryStats.notePhoneOff(); } else { mBatteryStats.notePhoneOn(); } } catch (RemoteException e) { /* The remote entity disappeared, we can safely ignore the exception. */ } finally { Binder.restoreCallingIdentity(ident); } Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); intent.putExtra(PhoneConstants.STATE_KEY, PhoneConstantConversions.convertCallState(state).toString()); if (!TextUtils.isEmpty(incomingNumber)) { intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); } // If a valid subId was specified, we should fire off a subId-specific state // change intent and include the subId. if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); } // If the phoneId is invalid, the broadcast is for overall call state. if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) { intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); } // Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast. intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); // Send broadcast twice, once for apps that have PRIVILEGED permission and once for those // that have the runtime one mContext.sendBroadcastAsUser(intent, UserHandle.ALL, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, android.Manifest.permission.READ_PHONE_STATE, AppOpsManager.OP_READ_PHONE_STATE); } private void broadcastDataConnectionStateChanged(int state, boolean isDataAllowed, String reason, String apn, String apnType, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, boolean roaming, int subId) { // Note: not reporting to the battery stats service here, because the // status bar takes care of that after taking into account all of the // required info. Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); intent.putExtra(PhoneConstants.STATE_KEY, PhoneConstantConversions.convertDataState(state).toString()); if (!isDataAllowed) { intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true); } if (reason != null) { intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason); } if (linkProperties != null) { intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties); String iface = linkProperties.getInterfaceName(); if (iface != null) { intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface); } } if (networkCapabilities != null) { intent.putExtra(PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY, networkCapabilities); } if (roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true); intent.putExtra(PhoneConstants.DATA_APN_KEY, apn); intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void broadcastDataConnectionFailed(String reason, String apnType, int subId) { Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason); intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState, int backgroundCallState, int disconnectCause, int preciseDisconnectCause) { Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED); intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState); intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState); intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState); intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause); intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, android.Manifest.permission.READ_PRECISE_PHONE_STATE); } private void broadcastPreciseDataConnectionStateChanged(int state, int networkType, String apnType, String apn, String reason, LinkProperties linkProperties, String failCause) { Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED); intent.putExtra(PhoneConstants.STATE_KEY, state); intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType); if (reason != null) intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason); if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn); if (linkProperties != null) { intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY,linkProperties); } if (failCause != null) intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause); mContext.sendBroadcastAsUser(intent, UserHandle.ALL, android.Manifest.permission.READ_PRECISE_PHONE_STATE); } private void enforceNotifyPermissionOrCarrierPrivilege(String method) { if (checkNotifyPermission()) { return; } enforceCarrierPrivilege(); } private boolean checkNotifyPermission(String method) { if (checkNotifyPermission()) { return true; } String msg = "Modify Phone State Permission Denial: " + method + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); if (DBG) log(msg); return false; } private boolean checkNotifyPermission() { return mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; } private void enforceCarrierPrivilege() { TelephonyManager tm = TelephonyManager.getDefault(); String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid()); for (String pkg : pkgs) { if (tm.checkCarrierPrivilegesForPackage(pkg) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { return; } } String msg = "Carrier Privilege Permission Denial: from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); if (DBG) log(msg); throw new SecurityException(msg); } private void checkListenerPermission(int events) { if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_COARSE_LOCATION, null); } if ((events & PhoneStateListener.LISTEN_CELL_INFO) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_COARSE_LOCATION, null); } if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) { try { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); // SKIP checking for run-time permission since caller or self has PRIVILEGED // permission } catch (SecurityException e) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PHONE_STATE, null); } } if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); } if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } } private void handleRemoveListLocked() { int size = mRemoveList.size(); if (VDBG) log("handleRemoveListLocked: mRemoveList.size()=" + size); if (size > 0) { for (IBinder b: mRemoveList) { remove(b); } mRemoveList.clear(); } } private boolean validateEventsAndUserLocked(Record r, int events) { int foregroundUser; long callingIdentity = Binder.clearCallingIdentity(); boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events); if (DBG | DBG_LOC) { log("validateEventsAndUserLocked: valid=" + valid + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser + " r.events=" + r.events + " events=" + events); } } finally { Binder.restoreCallingIdentity(callingIdentity); } return valid; } private boolean validatePhoneId(int phoneId) { boolean valid = (phoneId >= 0) && (phoneId < mNumPhones); if (VDBG) log("validatePhoneId: " + valid); return valid; } private static void log(String s) { Rlog.d(TAG, s); } boolean idMatch(int rSubId, int subId, int phoneId) { if(subId < 0) { // Invalid case, we need compare phoneId with default one. return (mDefaultPhoneId == phoneId); } if(rSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { return (subId == mDefaultSubId); } else { return (rSubId == subId); } } private void checkPossibleMissNotify(Record r, int phoneId) { int events = r.events; if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); r.callback.onServiceStateChanged( new ServiceState(mServiceState[phoneId])); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { try { SignalStrength signalStrength = mSignalStrength[phoneId]; if (DBG) { log("checkPossibleMissNotify: onSignalStrengthsChanged SS=" + signalStrength); } r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { int gsmSignalStrength = mSignalStrength[phoneId] .getGsmSignalStrength(); if (DBG) { log("checkPossibleMissNotify: onSignalStrengthChanged SS=" + gsmSignalStrength); } r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 : gsmSignalStrength)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { try { if (VDBG) { log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId=" + phoneId + " mwi=" + mMessageWaiting[phoneId]); } r.callback.onMessageWaitingIndicatorChanged( mMessageWaiting[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { try { if (VDBG) { log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId=" + phoneId + " cfi=" + mCallForwarding[phoneId]); } r.callback.onCallForwardingIndicatorChanged( mCallForwarding[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = " + mCellLocation[phoneId]); r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { try { if (DBG) { log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState" + "=" + mDataConnectionState[phoneId] + ", mDataConnectionNetworkType=" + mDataConnectionNetworkType[phoneId] + ")"); } r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); } catch (RemoteException ex) { mRemoveList.add(r.binder); } } } }