TelephonyRegistry.java revision 627bba736d022c39696b7c582a6af5592d2b8c33
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.pm.PackageManager;
22import android.os.Binder;
23import android.os.Bundle;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.telephony.CellLocation;
27import android.telephony.PhoneStateListener;
28import android.telephony.ServiceState;
29import android.telephony.TelephonyManager;
30import android.text.TextUtils;
31import android.util.Log;
32
33import java.util.ArrayList;
34import java.io.FileDescriptor;
35import java.io.PrintWriter;
36
37import com.android.internal.app.IBatteryStats;
38import com.android.internal.telephony.ITelephonyRegistry;
39import com.android.internal.telephony.IPhoneStateListener;
40import com.android.internal.telephony.DefaultPhoneNotifier;
41import com.android.internal.telephony.Phone;
42import com.android.internal.telephony.PhoneStateIntentReceiver;
43import com.android.internal.telephony.TelephonyIntents;
44import com.android.server.am.BatteryStatsService;
45
46
47/**
48 * Since phone process can be restarted, this class provides a centralized
49 * place that applications can register and be called back from.
50 */
51class TelephonyRegistry extends ITelephonyRegistry.Stub {
52    private static final String TAG = "TelephonyRegistry";
53
54    private static class Record {
55        String pkgForDebug;
56        IBinder binder;
57        IPhoneStateListener callback;
58        int events;
59    }
60
61    private final Context mContext;
62    private final ArrayList<Record> mRecords = new ArrayList();
63    private final IBatteryStats mBatteryStats;
64
65    private int mCallState = TelephonyManager.CALL_STATE_IDLE;
66    private String mCallIncomingNumber = "";
67    private ServiceState mServiceState = new ServiceState();
68    private int mSignalStrength = -1;
69    private boolean mMessageWaiting = false;
70    private boolean mCallForwarding = false;
71    private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
72    private int mDataConnectionState = TelephonyManager.DATA_CONNECTED;
73    private boolean mDataConnectionPossible = false;
74    private String mDataConnectionReason = "";
75    private String mDataConnectionApn = "";
76    private String mDataConnectionInterfaceName = "";
77    private Bundle mCellLocation = new Bundle();
78
79    // we keep a copy of all of the sate so we can send it out when folks register for it
80    //
81    // In these calls we call with the lock held.  This is safe becasuse remote
82    // calls go through a oneway interface and local calls going through a handler before
83    // they get to app code.
84
85    TelephonyRegistry(Context context) {
86        CellLocation.getEmpty().fillInNotifierBundle(mCellLocation);
87        mContext = context;
88        mBatteryStats = BatteryStatsService.getService();
89    }
90
91    public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
92            boolean notifyNow) {
93        //Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + Integer.toHexString(events));
94        if (events != 0) {
95            // check permissions
96            if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
97                mContext.enforceCallingOrSelfPermission(
98                        android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
99
100            }
101
102            synchronized (mRecords) {
103                // register
104                Record r = null;
105                find_and_add: {
106                    IBinder b = callback.asBinder();
107                    final int N = mRecords.size();
108                    for (int i=0; i<N; i++) {
109                        r = mRecords.get(i);
110                        if (b == r.binder) {
111                            break find_and_add;
112                        }
113                    }
114                    r = new Record();
115                    r.binder = b;
116                    r.callback = callback;
117                    r.pkgForDebug = pkgForDebug;
118                    mRecords.add(r);
119                }
120                int send = events & (events ^ r.events);
121                r.events = events;
122                if (notifyNow) {
123                    if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
124                        sendServiceState(r, mServiceState);
125                    }
126                    if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
127                        try {
128                            r.callback.onSignalStrengthChanged(mSignalStrength);
129                        } catch (RemoteException ex) {
130                            remove(r.binder);
131                        }
132                    }
133                    if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
134                        try {
135                            r.callback.onMessageWaitingIndicatorChanged(mMessageWaiting);
136                        } catch (RemoteException ex) {
137                            remove(r.binder);
138                        }
139                    }
140                    if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
141                        try {
142                            r.callback.onCallForwardingIndicatorChanged(mCallForwarding);
143                        } catch (RemoteException ex) {
144                            remove(r.binder);
145                        }
146                    }
147                    if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
148                        sendCellLocation(r, mCellLocation);
149                    }
150                    if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
151                        try {
152                            r.callback.onCallStateChanged(mCallState, mCallIncomingNumber);
153                        } catch (RemoteException ex) {
154                            remove(r.binder);
155                        }
156                    }
157                    if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
158                        try {
159                            r.callback.onDataConnectionStateChanged(mDataConnectionState);
160                        } catch (RemoteException ex) {
161                            remove(r.binder);
162                        }
163                    }
164                    if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
165                        try {
166                            r.callback.onDataActivity(mDataActivity);
167                        } catch (RemoteException ex) {
168                            remove(r.binder);
169                        }
170                    }
171                }
172            }
173        } else {
174            remove(callback.asBinder());
175        }
176    }
177
178    private void remove(IBinder binder) {
179        synchronized (mRecords) {
180            final int N = mRecords.size();
181            for (int i=0; i<N; i++) {
182                if (mRecords.get(i).binder == binder) {
183                    mRecords.remove(i);
184                    return;
185                }
186            }
187        }
188    }
189
190    public void notifyCallState(int state, String incomingNumber) {
191        if (!checkPhoneStatePermission("notifyCallState()")) {
192            return;
193        }
194        synchronized (mRecords) {
195            mCallState = state;
196            mCallIncomingNumber = incomingNumber;
197            final int N = mRecords.size();
198            for (int i=N-1; i>=0; i--) {
199                Record r = mRecords.get(i);
200                if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
201                    try {
202                        r.callback.onCallStateChanged(state, incomingNumber);
203                    } catch (RemoteException ex) {
204                        remove(r.binder);
205                    }
206                }
207            }
208        }
209        broadcastCallStateChanged(state, incomingNumber);
210    }
211
212    public void notifyServiceState(ServiceState state) {
213        if (!checkPhoneStatePermission("notifyServiceState()")) {
214            return;
215        }
216        synchronized (mRecords) {
217            mServiceState = state;
218            final int N = mRecords.size();
219            for (int i=N-1; i>=0; i--) {
220                Record r = mRecords.get(i);
221                if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
222                    sendServiceState(r, state);
223                }
224            }
225        }
226        broadcastServiceStateChanged(state);
227    }
228
229    public void notifySignalStrength(int signalStrengthASU) {
230        if (!checkPhoneStatePermission("notifySignalStrength()")) {
231            return;
232        }
233        synchronized (mRecords) {
234            mSignalStrength = signalStrengthASU;
235            final int N = mRecords.size();
236            for (int i=N-1; i>=0; i--) {
237                Record r = mRecords.get(i);
238                if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
239                    try {
240                        r.callback.onSignalStrengthChanged(signalStrengthASU);
241                    } catch (RemoteException ex) {
242                        remove(r.binder);
243                    }
244                }
245            }
246        }
247        broadcastSignalStrengthChanged(signalStrengthASU);
248    }
249
250    public void notifyMessageWaitingChanged(boolean mwi) {
251        if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) {
252            return;
253        }
254        synchronized (mRecords) {
255            mMessageWaiting = mwi;
256            final int N = mRecords.size();
257            for (int i=N-1; i>=0; i--) {
258                Record r = mRecords.get(i);
259                if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
260                    try {
261                        r.callback.onMessageWaitingIndicatorChanged(mwi);
262                    } catch (RemoteException ex) {
263                        remove(r.binder);
264                    }
265                }
266            }
267        }
268    }
269
270    public void notifyCallForwardingChanged(boolean cfi) {
271        if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) {
272            return;
273        }
274        synchronized (mRecords) {
275            mCallForwarding = cfi;
276            final int N = mRecords.size();
277            for (int i=N-1; i>=0; i--) {
278                Record r = mRecords.get(i);
279                if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
280                    try {
281                        r.callback.onCallForwardingIndicatorChanged(cfi);
282                    } catch (RemoteException ex) {
283                        remove(r.binder);
284                    }
285                }
286            }
287        }
288    }
289
290    public void notifyDataActivity(int state) {
291        if (!checkPhoneStatePermission("notifyDataActivity()")) {
292            return;
293        }
294        synchronized (mRecords) {
295            mDataActivity = state;
296            final int N = mRecords.size();
297            for (int i=N-1; i>=0; i--) {
298                Record r = mRecords.get(i);
299                if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
300                    try {
301                        r.callback.onDataActivity(state);
302                    } catch (RemoteException ex) {
303                        remove(r.binder);
304                    }
305                }
306            }
307        }
308    }
309
310    public void notifyDataConnection(int state, boolean isDataConnectivityPissible,
311            String reason, String apn, String interfaceName) {
312        if (!checkPhoneStatePermission("notifyDataConnection()")) {
313            return;
314        }
315        synchronized (mRecords) {
316            mDataConnectionState = state;
317            mDataConnectionPossible = isDataConnectivityPissible;
318            mDataConnectionReason = reason;
319            mDataConnectionApn = apn;
320            mDataConnectionInterfaceName = interfaceName;
321            final int N = mRecords.size();
322            for (int i=N-1; i>=0; i--) {
323                Record r = mRecords.get(i);
324                if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
325                    try {
326                        r.callback.onDataConnectionStateChanged(state);
327                    } catch (RemoteException ex) {
328                        remove(r.binder);
329                    }
330                }
331            }
332        }
333        broadcastDataConnectionStateChanged(state, isDataConnectivityPissible,
334                reason, apn, interfaceName);
335    }
336
337    public void notifyDataConnectionFailed(String reason) {
338        if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) {
339            return;
340        }
341        /*
342         * This is commented out because there is on onDataConnectionFailed callback
343         * on PhoneStateListener.  There should be.
344        synchronized (mRecords) {
345            mDataConnectionFailedReason = reason;
346            final int N = mRecords.size();
347            for (int i=N-1; i>=0; i--) {
348                Record r = mRecords.get(i);
349                if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_FAILED) != 0) {
350                    // XXX
351                }
352            }
353        }
354        */
355        broadcastDataConnectionFailed(reason);
356    }
357
358    public void notifyCellLocation(Bundle cellLocation) {
359        if (!checkPhoneStatePermission("notifyCellLocation()")) {
360            return;
361        }
362        synchronized (mRecords) {
363            mCellLocation = cellLocation;
364            final int N = mRecords.size();
365            for (int i=N-1; i>=0; i--) {
366                Record r = mRecords.get(i);
367                if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
368                    sendCellLocation(r, cellLocation);
369                }
370            }
371        }
372    }
373
374    //
375    // the new callback broadcasting
376    //
377    // copy the service state object so they can't mess it up in the local calls
378    //
379    public void sendServiceState(Record r, ServiceState state) {
380        try {
381            r.callback.onServiceStateChanged(new ServiceState(state));
382        } catch (RemoteException ex) {
383            remove(r.binder);
384        }
385    }
386
387    public void sendCellLocation(Record r, Bundle cellLocation) {
388        try {
389            r.callback.onCellLocationChanged(new Bundle(cellLocation));
390        } catch (RemoteException ex) {
391            remove(r.binder);
392        }
393    }
394
395
396    @Override
397    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
398        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
399                != PackageManager.PERMISSION_GRANTED) {
400            pw.println("Permission Denial: can't dump telephony.registry from from pid="
401                    + Binder.getCallingPid()
402                    + ", uid=" + Binder.getCallingUid());
403            return;
404        }
405        synchronized (mRecords) {
406            final int N = mRecords.size();
407            pw.println("last known state:");
408            pw.println("  mCallState=" + mCallState);
409            pw.println("  mCallIncomingNumber=" + mCallIncomingNumber);
410            pw.println("  mServiceState=" + mServiceState);
411            pw.println("  mSignalStrength=" + mSignalStrength);
412            pw.println("  mMessageWaiting=" + mMessageWaiting);
413            pw.println("  mCallForwarding=" + mCallForwarding);
414            pw.println("  mDataActivity=" + mDataActivity);
415            pw.println("  mDataConnectionState=" + mDataConnectionState);
416            pw.println("  mDataConnectionPossible=" + mDataConnectionPossible);
417            pw.println("  mDataConnectionReason=" + mDataConnectionReason);
418            pw.println("  mDataConnectionApn=" + mDataConnectionApn);
419            pw.println("  mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
420            pw.println("  mCellLocation=" + mCellLocation);
421            pw.println("registrations: count=" + N);
422            for (int i=0; i<N; i++) {
423                Record r = mRecords.get(i);
424                pw.println("  " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
425            }
426        }
427    }
428
429
430    //
431    // the legacy intent broadcasting
432    //
433
434    private void broadcastServiceStateChanged(ServiceState state) {
435        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
436        Bundle data = new Bundle();
437        state.fillInNotifierBundle(data);
438        intent.putExtras(data);
439        mContext.sendStickyBroadcast(intent);
440    }
441
442    private void broadcastSignalStrengthChanged(int asu) {
443        long ident = Binder.clearCallingIdentity();
444        try {
445            mBatteryStats.notePhoneSignalStrength(asu);
446        } catch (RemoteException e) {
447        } finally {
448            Binder.restoreCallingIdentity(ident);
449        }
450
451        Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
452        intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu);
453        mContext.sendStickyBroadcast(intent);
454    }
455
456    private void broadcastCallStateChanged(int state, String incomingNumber) {
457        long ident = Binder.clearCallingIdentity();
458        try {
459            if (state == TelephonyManager.CALL_STATE_IDLE) {
460                mBatteryStats.notePhoneOff();
461            } else {
462                mBatteryStats.notePhoneOn();
463            }
464        } catch (RemoteException e) {
465        } finally {
466            Binder.restoreCallingIdentity(ident);
467        }
468
469        Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
470        intent.putExtra(Phone.STATE_KEY,
471                DefaultPhoneNotifier.convertCallState(state).toString());
472        if (!TextUtils.isEmpty(incomingNumber)) {
473            intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
474        }
475        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
476    }
477
478    private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible,
479            String reason, String apn, String interfaceName) {
480        // Note: not reporting to the battery stats service here, because the
481        // status bar takes care of that after taking into account all of the
482        // required info.
483        Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
484        intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString());
485        if (!isDataConnectivityPossible) {
486            intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true);
487        }
488        if (reason != null) {
489            intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
490        }
491        intent.putExtra(Phone.DATA_APN_KEY, apn);
492        intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
493        mContext.sendStickyBroadcast(intent);
494    }
495
496    private void broadcastDataConnectionFailed(String reason) {
497        Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
498        intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
499        mContext.sendStickyBroadcast(intent);
500    }
501
502    private boolean checkPhoneStatePermission(String method) {
503        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
504                == PackageManager.PERMISSION_GRANTED) {
505            return true;
506        }
507        String msg = "Modify Phone State Permission Denial: " + method + " from pid="
508                + Binder.getCallingPid()
509                + ", uid=" + Binder.getCallingUid();
510        Log.w(TAG, msg);
511        return false;
512    }
513}
514