blob: e609bd7889d3903bba326b0473d1cb862d8e49d4 [file] [log] [blame]
/*
* 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<IBinder> mRemoveList = new ArrayList<IBinder>();
private final ArrayList<Record> mRecords = new ArrayList<Record>();
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<String>[] mConnectedApns;
private LinkProperties[] mDataConnectionLinkProperties;
private NetworkCapabilities[] mDataConnectionNetworkCapabilities;
private Bundle[] mCellLocation;
private int[] mDataConnectionNetworkType;
private int mOtaspMode = TelephonyManager.OTASP_UNKNOWN;
private ArrayList<List<CellInfo>> 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<List<CellInfo>>();
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<String>();
}
// 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> cellInfo) {
notifyCellInfoForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellInfo);
}
public void notifyCellInfoForSubscriber(int subId, List<CellInfo> 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);
}
}
}
}