blob: d4565b61bca142b05917c1051fdca55a20a64d07 [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.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
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.CellLocation;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.CellInfo;
import android.telephony.TelephonyManager;
import android.telephony.DisconnectCause;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.PreciseDisconnectCause;
import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
import java.util.List;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyIntents;
import com.android.server.am.BatteryStatsService;
/**
* Since phone process can be restarted, this class provides a centralized place
* that applications can register and be called back from.
*/
class TelephonyRegistry extends ITelephonyRegistry.Stub {
private static final String TAG = "TelephonyRegistry";
private static final boolean DBG = false;
private static final boolean DBG_LOC = false;
private static class Record {
String pkgForDebug;
IBinder binder;
IPhoneStateListener callback;
int callerUid;
int events;
@Override
public String toString() {
return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid +
" events=" + Integer.toHexString(events) + "}";
}
}
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 int mCallState = TelephonyManager.CALL_STATE_IDLE;
private String mCallIncomingNumber = "";
private ServiceState mServiceState = new ServiceState();
private SignalStrength mSignalStrength = new SignalStrength();
private boolean mMessageWaiting = false;
private boolean mCallForwarding = false;
private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
private int mDataConnectionState = TelephonyManager.DATA_UNKNOWN;
private boolean mDataConnectionPossible = false;
private String mDataConnectionReason = "";
private String mDataConnectionApn = "";
private ArrayList<String> mConnectedApns;
private LinkProperties mDataConnectionLinkProperties;
private LinkCapabilities mDataConnectionLinkCapabilities;
private Bundle mCellLocation = new Bundle();
private int mDataConnectionNetworkType;
private int mOtaspMode = ServiceStateTracker.OTASP_UNKNOWN;
private List<CellInfo> mCellInfo = null;
private DataConnectionRealTimeInfo mDcRtInfo = new DataConnectionRealTimeInfo();
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 PreciseDataConnectionState mPreciseDataConnectionState =
new PreciseDataConnectionState();
static final int PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_DATA_ACTIVITY |
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
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 final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_SWITCHED: {
if (DBG) Slog.d(TAG, "MSG_USER_SWITCHED userId=" + msg.arg1);
TelephonyRegistry.this.notifyCellLocation(mCellLocation);
break;
}
}
}
};
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
}
}
};
// 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();
// Note that location can be null for non-phone builds like
// like the generic one.
if (location != null) {
location.fillInNotifierBundle(mCellLocation);
}
mContext = context;
mBatteryStats = BatteryStatsService.getService();
mConnectedApns = new ArrayList<String>();
}
public void systemRunning() {
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mBroadcastReceiver, filter);
}
@Override
public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
boolean notifyNow) {
int callerUid = UserHandle.getCallingUserId();
int myUid = UserHandle.myUserId();
if (DBG) {
Slog.d(TAG, "listen: E pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events)
+ " myUid=" + myUid
+ " callerUid=" + callerUid);
}
if (events != 0) {
/* Checks permission and throws Security exception */
checkListenerPermission(events);
synchronized (mRecords) {
// register
Record r = null;
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;
r.callback = callback;
r.pkgForDebug = pkgForDebug;
r.callerUid = callerUid;
mRecords.add(r);
if (DBG) Slog.i(TAG, "listen: add new record=" + r);
}
int send = events & (events ^ r.events);
r.events = events;
if (notifyNow) {
if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
try {
r.callback.onServiceStateChanged(new ServiceState(mServiceState));
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
try {
int gsmSignalStrength = mSignalStrength.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);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
try {
r.callback.onCallForwardingIndicatorChanged(mCallForwarding);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
try {
if (DBG_LOC) Slog.d(TAG, "listen: mCellLocation=" + mCellLocation);
r.callback.onCellLocationChanged(new Bundle(mCellLocation));
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try {
r.callback.onCallStateChanged(mCallState, mCallIncomingNumber);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onDataConnectionStateChanged(mDataConnectionState,
mDataConnectionNetworkType);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
try {
r.callback.onDataActivity(mDataActivity);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
try {
r.callback.onSignalStrengthsChanged(mSignalStrength);
} 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) Slog.d(TAG, "listen: mCellInfo=" + mCellInfo);
r.callback.onCellInfoChanged(mCellInfo);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
try {
r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo);
} 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);
}
}
}
}
} else {
remove(callback.asBinder());
}
}
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) {
mRecords.remove(i);
return;
}
}
}
}
public void notifyCallState(int state, String incomingNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
synchronized (mRecords) {
mCallState = state;
mCallIncomingNumber = incomingNumber;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try {
r.callback.onCallStateChanged(state, incomingNumber);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastCallStateChanged(state, incomingNumber);
}
public void notifyServiceState(ServiceState state) {
if (!checkNotifyPermission("notifyServiceState()")){
return;
}
long ident = Binder.clearCallingIdentity();
try {
mBatteryStats.notePhoneState(state.getState());
} catch (RemoteException re) {
// Can't do much
} finally {
Binder.restoreCallingIdentity(ident);
}
synchronized (mRecords) {
mServiceState = state;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
try {
r.callback.onServiceStateChanged(new ServiceState(state));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastServiceStateChanged(state);
}
public void notifySignalStrength(SignalStrength signalStrength) {
if (!checkNotifyPermission("notifySignalStrength()")) {
return;
}
synchronized (mRecords) {
mSignalStrength = signalStrength;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
try {
r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
: gsmSignalStrength));
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastSignalStrengthChanged(signalStrength);
}
public void notifyCellInfo(List<CellInfo> cellInfo) {
if (!checkNotifyPermission("notifyCellInfo()")) {
return;
}
synchronized (mRecords) {
mCellInfo = cellInfo;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
try {
if (DBG_LOC) {
Slog.d(TAG, "notifyCellInfo: mCellInfo=" + mCellInfo + " r=" + r);
}
r.callback.onCellInfoChanged(cellInfo);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
}
public void notifyDataConnectionRealTimeInfo(DataConnectionRealTimeInfo dcRtInfo) {
if (!checkNotifyPermission("notifyDataConnectionRealTimeInfo()")) {
return;
}
synchronized (mRecords) {
mDcRtInfo = dcRtInfo;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r,
PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO)) {
try {
if (DBG_LOC) {
Slog.d(TAG, "notifyDataConnectionRealTimeInfo: mDcRtInfo="
+ mDcRtInfo + " r=" + r);
}
r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
}
public void notifyMessageWaitingChanged(boolean mwi) {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
return;
}
synchronized (mRecords) {
mMessageWaiting = mwi;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
}
public void notifyCallForwardingChanged(boolean cfi) {
if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
return;
}
synchronized (mRecords) {
mCallForwarding = cfi;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
}
public void notifyDataActivity(int state) {
if (!checkNotifyPermission("notifyDataActivity()" )) {
return;
}
synchronized (mRecords) {
mDataActivity = state;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
try {
r.callback.onDataActivity(state);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
LinkCapabilities linkCapabilities, int networkType, boolean roaming) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
if (DBG) {
Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible="
+ isDataConnectivityPossible + " reason='" + reason
+ "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
+ " mRecords.size()=" + mRecords.size() + " mRecords=" + mRecords);
}
synchronized (mRecords) {
boolean modified = false;
if (state == TelephonyManager.DATA_CONNECTED) {
if (!mConnectedApns.contains(apnType)) {
mConnectedApns.add(apnType);
if (mDataConnectionState != state) {
mDataConnectionState = state;
modified = true;
}
}
} else {
if (mConnectedApns.remove(apnType)) {
if (mConnectedApns.isEmpty()) {
mDataConnectionState = state;
modified = true;
} else {
// leave mDataConnectionState as is and
// send out the new status for the APN in question.
}
}
}
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
mDataConnectionLinkProperties = linkProperties;
mDataConnectionLinkCapabilities = linkCapabilities;
if (mDataConnectionNetworkType != networkType) {
mDataConnectionNetworkType = networkType;
// need to tell registered listeners about the new network type
modified = true;
}
if (modified) {
if (DBG) {
Slog.d(TAG, "onDataConnectionStateChanged(" + mDataConnectionState
+ ", " + mDataConnectionNetworkType + ")");
}
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onDataConnectionStateChanged(mDataConnectionState,
mDataConnectionNetworkType);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
apnType, apn, reason, linkProperties, "");
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
apnType, linkProperties, linkCapabilities, roaming);
broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
linkProperties, "");
}
public void notifyDataConnectionFailed(String reason, String apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
}
synchronized (mRecords) {
mPreciseDataConnectionState = new PreciseDataConnectionState(
TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
apnType, "", reason, null, "");
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastDataConnectionFailed(reason, apnType);
broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, "");
}
public void notifyCellLocation(Bundle cellLocation) {
if (!checkNotifyPermission("notifyCellLocation()")) {
return;
}
synchronized (mRecords) {
mCellLocation = cellLocation;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
try {
if (DBG_LOC) {
Slog.d(TAG, "notifyCellLocation: mCellLocation=" + mCellLocation
+ " 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.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
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.events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
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.events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
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.events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
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);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump telephony.registry from from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
synchronized (mRecords) {
final int recordCount = mRecords.size();
pw.println("last known state:");
pw.println(" mCallState=" + mCallState);
pw.println(" mCallIncomingNumber=" + mCallIncomingNumber);
pw.println(" mServiceState=" + mServiceState);
pw.println(" mSignalStrength=" + mSignalStrength);
pw.println(" mMessageWaiting=" + mMessageWaiting);
pw.println(" mCallForwarding=" + mCallForwarding);
pw.println(" mDataActivity=" + mDataActivity);
pw.println(" mDataConnectionState=" + mDataConnectionState);
pw.println(" mDataConnectionPossible=" + mDataConnectionPossible);
pw.println(" mDataConnectionReason=" + mDataConnectionReason);
pw.println(" mDataConnectionApn=" + mDataConnectionApn);
pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
pw.println(" mCellLocation=" + mCellLocation);
pw.println(" mCellInfo=" + mCellInfo);
pw.println(" mDcRtInfo=" + mDcRtInfo);
pw.println("registrations: count=" + recordCount);
for (Record r : mRecords) {
pw.println(" " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
}
}
}
//
// the legacy intent broadcasting
//
private void broadcastServiceStateChanged(ServiceState state) {
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
Bundle data = new Bundle();
state.fillInNotifierBundle(data);
intent.putExtras(data);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastSignalStrengthChanged(SignalStrength signalStrength) {
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);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
Bundle data = new Bundle();
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastCallStateChanged(int state, String incomingNumber) {
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,
DefaultPhoneNotifier.convertCallState(state).toString());
if (!TextUtils.isEmpty(incomingNumber)) {
intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
}
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PHONE_STATE);
}
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
LinkCapabilities linkCapabilities, boolean roaming) {
// 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,
DefaultPhoneNotifier.convertDataState(state).toString());
if (!isDataConnectivityPossible) {
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 (linkCapabilities != null) {
intent.putExtra(PhoneConstants.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
}
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);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastDataConnectionFailed(String reason, String apnType) {
Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
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 boolean checkNotifyPermission(String method) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED) {
return true;
}
String msg = "Modify Phone State Permission Denial: " + method + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
if (DBG) Slog.w(TAG, msg);
return false;
}
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 & PHONE_STATE_PERMISSION_MASK) != 0) {
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);
}
}
private void handleRemoveListLocked() {
if (mRemoveList.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.callerUid == foregroundUser && (r.events & events) != 0;
if (DBG | DBG_LOC) {
Slog.d(TAG, "validateEventsAndUserLocked: valid=" + valid
+ " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
+ " r.events=" + r.events + " events=" + events);
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
return valid;
}
}