summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/TelephonyRegistry.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
commit9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch)
treed88beb88001f2482911e3d28e43833b50e4b4e97 /services/java/com/android/server/TelephonyRegistry.java
parentd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff)
downloadframeworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz
frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'services/java/com/android/server/TelephonyRegistry.java')
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java462
1 files changed, 462 insertions, 0 deletions
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
new file mode 100644
index 0000000..5e5fb93
--- /dev/null
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -0,0 +1,462 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+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.Phone;
+import com.android.internal.telephony.PhoneStateIntentReceiver;
+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 class Record {
+ String pkgForDebug;
+ IBinder binder;
+ IPhoneStateListener callback;
+ int events;
+ }
+
+ private final Context mContext;
+ private final ArrayList<Record> mRecords = new ArrayList();
+ private final IBatteryStats mBatteryStats;
+
+ private int mCallState = TelephonyManager.CALL_STATE_IDLE;
+ private String mCallIncomingNumber = "";
+ private ServiceState mServiceState = new ServiceState();
+ private int mSignalStrength = -1;
+ private boolean mMessageWaiting = false;
+ private boolean mCallForwarding = false;
+ private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+ private int mDataConnectionState = TelephonyManager.DATA_CONNECTED;
+ private boolean mDataConnectionPossible = false;
+ private String mDataConnectionReason = "";
+ private String mDataConnectionApn = "";
+ private String mDataConnectionInterfaceName = "";
+ private Bundle mCellLocation = new Bundle();
+
+ // we keep a copy of all of the sate 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.getEmpty().fillInNotifierBundle(mCellLocation);
+ mContext = context;
+ mBatteryStats = BatteryStatsService.getService();
+ }
+
+ public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
+ boolean notifyNow) {
+ //Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events));
+ if (events != 0) {
+ // check permissions
+ if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
+
+ }
+
+ 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;
+ mRecords.add(r);
+ }
+ int send = events & (events ^ r.events);
+ r.events = events;
+ if (notifyNow) {
+ if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
+ sendServiceState(r, mServiceState);
+ }
+ if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
+ try {
+ r.callback.onSignalStrengthChanged(mSignalStrength);
+ } 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 ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
+ sendCellLocation(r, mCellLocation);
+ }
+ 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);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
+ try {
+ r.callback.onDataActivity(mDataActivity);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ } else {
+ remove(callback.asBinder());
+ }
+ }
+
+ private void remove(IBinder binder) {
+ synchronized (mRecords) {
+ final int N = mRecords.size();
+ for (int i=0; i<N; i++) {
+ if (mRecords.get(i).binder == binder) {
+ mRecords.remove(i);
+ return;
+ }
+ }
+ }
+ }
+
+ public void notifyCallState(int state, String incomingNumber) {
+ synchronized (mRecords) {
+ mCallState = state;
+ mCallIncomingNumber = incomingNumber;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
+ try {
+ r.callback.onCallStateChanged(state, incomingNumber);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ broadcastCallStateChanged(state, incomingNumber);
+ }
+
+ public void notifyServiceState(ServiceState state) {
+ synchronized (mRecords) {
+ mServiceState = state;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
+ sendServiceState(r, state);
+ }
+ }
+ }
+ broadcastServiceStateChanged(state);
+ }
+
+ public void notifySignalStrength(int signalStrengthASU) {
+ synchronized (mRecords) {
+ mSignalStrength = signalStrengthASU;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
+ try {
+ r.callback.onSignalStrengthChanged(signalStrengthASU);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ broadcastSignalStrengthChanged(signalStrengthASU);
+ }
+
+ public void notifyMessageWaitingChanged(boolean mwi) {
+ synchronized (mRecords) {
+ mMessageWaiting = mwi;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
+ try {
+ r.callback.onMessageWaitingIndicatorChanged(mwi);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ }
+
+ public void notifyCallForwardingChanged(boolean cfi) {
+ synchronized (mRecords) {
+ mCallForwarding = cfi;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
+ try {
+ r.callback.onCallForwardingIndicatorChanged(cfi);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ }
+
+ public void notifyDataActivity(int state) {
+ synchronized (mRecords) {
+ mDataActivity = state;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
+ try {
+ r.callback.onDataActivity(state);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ }
+
+ public void notifyDataConnection(int state, boolean isDataConnectivityPissible,
+ String reason, String apn, String interfaceName) {
+ synchronized (mRecords) {
+ mDataConnectionState = state;
+ mDataConnectionPossible = isDataConnectivityPissible;
+ mDataConnectionReason = reason;
+ mDataConnectionApn = apn;
+ mDataConnectionInterfaceName = interfaceName;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+ try {
+ r.callback.onDataConnectionStateChanged(state);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+ }
+ broadcastDataConnectionStateChanged(state, isDataConnectivityPissible,
+ reason, apn, interfaceName);
+ }
+
+ public void notifyDataConnectionFailed(String reason) {
+ /*
+ * This is commented out because there is on onDataConnectionFailed callback
+ * on PhoneStateListener. There should be.
+ synchronized (mRecords) {
+ mDataConnectionFailedReason = reason;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_FAILED) != 0) {
+ // XXX
+ }
+ }
+ }
+ */
+ broadcastDataConnectionFailed(reason);
+ }
+
+ public void notifyCellLocation(Bundle cellLocation) {
+ synchronized (mRecords) {
+ mCellLocation = cellLocation;
+ final int N = mRecords.size();
+ for (int i=N-1; i>=0; i--) {
+ Record r = mRecords.get(i);
+ if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
+ sendCellLocation(r, cellLocation);
+ }
+ }
+ }
+ }
+
+ //
+ // the new callback broadcasting
+ //
+ // copy the service state object so they can't mess it up in the local calls
+ //
+ public void sendServiceState(Record r, ServiceState state) {
+ try {
+ r.callback.onServiceStateChanged(new ServiceState(state));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+
+ public void sendCellLocation(Record r, Bundle cellLocation) {
+ try {
+ r.callback.onCellLocationChanged(new Bundle(cellLocation));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+
+
+ @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 N = 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(" mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
+ pw.println(" mCellLocation=" + mCellLocation);
+ pw.println("registrations: count=" + N);
+ for (int i=0; i<N; i++) {
+ Record r = mRecords.get(i);
+ 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.sendStickyBroadcast(intent);
+ }
+
+ private void broadcastSignalStrengthChanged(int asu) {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
+ intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ 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) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ intent.putExtra(Phone.STATE_KEY,
+ DefaultPhoneNotifier.convertCallState(state).toString());
+ if (!TextUtils.isEmpty(incomingNumber)) {
+ intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
+ }
+ mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+ }
+
+ private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible,
+ String reason, String apn, String interfaceName) {
+ Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+ intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString());
+ if (!isDataConnectivityPossible) {
+ intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true);
+ }
+ if (reason != null) {
+ intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
+ }
+ intent.putExtra(Phone.DATA_APN_KEY, apn);
+ intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void broadcastDataConnectionFailed(String reason) {
+ Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
+ intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
+ mContext.sendStickyBroadcast(intent);
+ }
+}