TelephonyRegistry.java revision f61101f6266be243c481d163b95e65d67b8d1669
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.net.LinkCapabilities;
23import android.net.LinkProperties;
24import android.os.Binder;
25import android.os.Bundle;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.telephony.CellLocation;
29import android.telephony.PhoneStateListener;
30import android.telephony.ServiceState;
31import android.telephony.SignalStrength;
32import android.telephony.TelephonyManager;
33import android.text.TextUtils;
34import android.util.Slog;
35
36import java.util.ArrayList;
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.net.NetworkInterface;
40
41import com.android.internal.app.IBatteryStats;
42import com.android.internal.telephony.ITelephonyRegistry;
43import com.android.internal.telephony.IPhoneStateListener;
44import com.android.internal.telephony.DefaultPhoneNotifier;
45import com.android.internal.telephony.Phone;
46import com.android.internal.telephony.TelephonyIntents;
47import com.android.server.am.BatteryStatsService;
48
49/**
50 * Since phone process can be restarted, this class provides a centralized place
51 * that applications can register and be called back from.
52 */
53class TelephonyRegistry extends ITelephonyRegistry.Stub {
54    private static final String TAG = "TelephonyRegistry";
55
56    private static class Record {
57        String pkgForDebug;
58
59        IBinder binder;
60
61        IPhoneStateListener callback;
62
63        int events;
64    }
65
66    private final Context mContext;
67
68    private final ArrayList<Record> mRecords = new ArrayList();
69
70    private final IBatteryStats mBatteryStats;
71
72    private int mCallState = TelephonyManager.CALL_STATE_IDLE;
73
74    private String mCallIncomingNumber = "";
75
76    private ServiceState mServiceState = new ServiceState();
77
78    private SignalStrength mSignalStrength = new SignalStrength();
79
80    private boolean mMessageWaiting = false;
81
82    private boolean mCallForwarding = false;
83
84    private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
85
86    private int mDataConnectionState = TelephonyManager.DATA_CONNECTED;
87
88    private boolean mDataConnectionPossible = false;
89
90    private String mDataConnectionReason = "";
91
92    private String mDataConnectionApn = "";
93
94    private ArrayList<String> mConnectedApns;
95
96    private LinkProperties mDataConnectionLinkProperties;
97
98    private LinkCapabilities mDataConnectionLinkCapabilities;
99
100    private Bundle mCellLocation = new Bundle();
101
102    private int mDataConnectionNetworkType;
103
104    static final int PHONE_STATE_PERMISSION_MASK =
105                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
106                PhoneStateListener.LISTEN_CALL_STATE |
107                PhoneStateListener.LISTEN_DATA_ACTIVITY |
108                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
109                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
110
111    // we keep a copy of all of the state so we can send it out when folks
112    // register for it
113    //
114    // In these calls we call with the lock held. This is safe becasuse remote
115    // calls go through a oneway interface and local calls going through a
116    // handler before they get to app code.
117
118    TelephonyRegistry(Context context) {
119        CellLocation  location = CellLocation.getEmpty();
120
121        // Note that location can be null for non-phone builds like
122        // like the generic one.
123        if (location != null) {
124            location.fillInNotifierBundle(mCellLocation);
125        }
126        mContext = context;
127        mBatteryStats = BatteryStatsService.getService();
128        mConnectedApns = new ArrayList<String>();
129    }
130
131    public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
132            boolean notifyNow) {
133        // Slog.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" +
134        // Integer.toHexString(events));
135        if (events != 0) {
136            /* Checks permission and throws Security exception */
137            checkListenerPermission(events);
138
139            synchronized (mRecords) {
140                // register
141                Record r = null;
142                find_and_add: {
143                    IBinder b = callback.asBinder();
144                    final int N = mRecords.size();
145                    for (int i = 0; i < N; i++) {
146                        r = mRecords.get(i);
147                        if (b == r.binder) {
148                            break find_and_add;
149                        }
150                    }
151                    r = new Record();
152                    r.binder = b;
153                    r.callback = callback;
154                    r.pkgForDebug = pkgForDebug;
155                    mRecords.add(r);
156                }
157                int send = events & (events ^ r.events);
158                r.events = events;
159                if (notifyNow) {
160                    if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
161                        sendServiceState(r, mServiceState);
162                    }
163                    if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
164                        try {
165                            int gsmSignalStrength = mSignalStrength.getGsmSignalStrength();
166                            r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
167                                    : gsmSignalStrength));
168                        } catch (RemoteException ex) {
169                            remove(r.binder);
170                        }
171                    }
172                    if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
173                        try {
174                            r.callback.onMessageWaitingIndicatorChanged(mMessageWaiting);
175                        } catch (RemoteException ex) {
176                            remove(r.binder);
177                        }
178                    }
179                    if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
180                        try {
181                            r.callback.onCallForwardingIndicatorChanged(mCallForwarding);
182                        } catch (RemoteException ex) {
183                            remove(r.binder);
184                        }
185                    }
186                    if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
187                        sendCellLocation(r, mCellLocation);
188                    }
189                    if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
190                        try {
191                            r.callback.onCallStateChanged(mCallState, mCallIncomingNumber);
192                        } catch (RemoteException ex) {
193                            remove(r.binder);
194                        }
195                    }
196                    if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
197                        try {
198                            r.callback.onDataConnectionStateChanged(mDataConnectionState,
199                                mDataConnectionNetworkType);
200                        } catch (RemoteException ex) {
201                            remove(r.binder);
202                        }
203                    }
204                    if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
205                        try {
206                            r.callback.onDataActivity(mDataActivity);
207                        } catch (RemoteException ex) {
208                            remove(r.binder);
209                        }
210                    }
211                    if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
212                        try {
213                            r.callback.onSignalStrengthsChanged(mSignalStrength);
214                        } catch (RemoteException ex) {
215                            remove(r.binder);
216                        }
217                    }
218                }
219            }
220        } else {
221            remove(callback.asBinder());
222        }
223    }
224
225    private void remove(IBinder binder) {
226        synchronized (mRecords) {
227            final int recordCount = mRecords.size();
228            for (int i = 0; i < recordCount; i++) {
229                if (mRecords.get(i).binder == binder) {
230                    mRecords.remove(i);
231                    return;
232                }
233            }
234        }
235    }
236
237    public void notifyCallState(int state, String incomingNumber) {
238        if (!checkNotifyPermission("notifyCallState()")) {
239            return;
240        }
241        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
242        synchronized (mRecords) {
243            mCallState = state;
244            mCallIncomingNumber = incomingNumber;
245            for (Record r : mRecords) {
246                if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
247                    try {
248                        r.callback.onCallStateChanged(state, incomingNumber);
249                    } catch (RemoteException ex) {
250                        removeList.add(r.binder);
251                    }
252                }
253            }
254            for (IBinder b : removeList) remove(b);
255        }
256        broadcastCallStateChanged(state, incomingNumber);
257    }
258
259    public void notifyServiceState(ServiceState state) {
260        if (!checkNotifyPermission("notifyServiceState()")){
261            return;
262        }
263        synchronized (mRecords) {
264            mServiceState = state;
265            for (Record r : mRecords) {
266                if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
267                    sendServiceState(r, state);
268                }
269            }
270        }
271        broadcastServiceStateChanged(state);
272    }
273
274    public void notifySignalStrength(SignalStrength signalStrength) {
275        if (!checkNotifyPermission("notifySignalStrength()")) {
276            return;
277        }
278        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
279        synchronized (mRecords) {
280            mSignalStrength = signalStrength;
281            for (Record r : mRecords) {
282                if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
283                    sendSignalStrength(r, signalStrength);
284                }
285                if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
286                    try {
287                        int gsmSignalStrength = signalStrength.getGsmSignalStrength();
288                        r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
289                                : gsmSignalStrength));
290                    } catch (RemoteException ex) {
291                        removeList.add(r.binder);
292                    }
293                }
294            }
295            for (IBinder b : removeList) remove(b);
296        }
297        broadcastSignalStrengthChanged(signalStrength);
298    }
299
300    public void notifyMessageWaitingChanged(boolean mwi) {
301        if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
302            return;
303        }
304        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
305        synchronized (mRecords) {
306            mMessageWaiting = mwi;
307            for (Record r : mRecords) {
308                if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
309                    try {
310                        r.callback.onMessageWaitingIndicatorChanged(mwi);
311                    } catch (RemoteException ex) {
312                        removeList.add(r.binder);
313                    }
314                }
315            }
316            for (IBinder b : removeList) remove(b);
317        }
318    }
319
320    public void notifyCallForwardingChanged(boolean cfi) {
321        if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
322            return;
323        }
324        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
325        synchronized (mRecords) {
326            mCallForwarding = cfi;
327            for (Record r : mRecords) {
328                if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
329                    try {
330                        r.callback.onCallForwardingIndicatorChanged(cfi);
331                    } catch (RemoteException ex) {
332                        removeList.add(r.binder);
333                    }
334                }
335            }
336            for (IBinder b : removeList) remove(b);
337        }
338    }
339
340    public void notifyDataActivity(int state) {
341        if (!checkNotifyPermission("notifyDataActivity()" )) {
342            return;
343        }
344        ArrayList<IBinder> removeList = new ArrayList<IBinder>();
345        synchronized (mRecords) {
346            mDataActivity = state;
347            for (Record r : mRecords) {
348                if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
349                    try {
350                        r.callback.onDataActivity(state);
351                    } catch (RemoteException ex) {
352                        removeList.add(r.binder);
353                    }
354                }
355            }
356            for (IBinder b : removeList) remove(b);
357        }
358    }
359
360    public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
361            String reason, String apn, String apnType, LinkProperties linkProperties,
362            LinkCapabilities linkCapabilities, int networkType) {
363        if (!checkNotifyPermission("notifyDataConnection()" )) {
364            return;
365        }
366        synchronized (mRecords) {
367            boolean modified = false;
368            if (state == TelephonyManager.DATA_CONNECTED) {
369                if (!mConnectedApns.contains(apnType)) {
370                    mConnectedApns.add(apnType);
371                    if (mDataConnectionState != state) {
372                        mDataConnectionState = state;
373                        modified = true;
374                    }
375                }
376            } else {
377                mConnectedApns.remove(apnType);
378                if (mConnectedApns.isEmpty()) {
379                    mDataConnectionState = state;
380                    modified = true;
381                } else {
382                    // leave mDataConnectionState as is and
383                    // send out the new status for the APN in question.
384                }
385            }
386            mDataConnectionPossible = isDataConnectivityPossible;
387            mDataConnectionReason = reason;
388            mDataConnectionLinkProperties = linkProperties;
389            mDataConnectionLinkCapabilities = linkCapabilities;
390            if (mDataConnectionNetworkType != networkType) {
391                mDataConnectionNetworkType = networkType;
392                modified = true;
393            }
394            if (modified) {
395                ArrayList<IBinder> removeList = new ArrayList<IBinder>();
396                for (Record r : mRecords) {
397                    if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
398                        try {
399                            r.callback.onDataConnectionStateChanged(state, networkType);
400                        } catch (RemoteException ex) {
401                            removeList.add(r.binder);
402                        }
403                    }
404                }
405                for (IBinder b : removeList) remove(b);
406            }
407        }
408        broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
409                apnType, linkProperties, linkCapabilities);
410    }
411
412    public void notifyDataConnectionFailed(String reason, String apnType) {
413        if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
414            return;
415        }
416        /*
417         * This is commented out because there is no onDataConnectionFailed callback
418         * in PhoneStateListener. There should be.
419        synchronized (mRecords) {
420            mDataConnectionFailedReason = reason;
421            final int N = mRecords.size();
422            for (int i=N-1; i>=0; i--) {
423                Record r = mRecords.get(i);
424                if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_FAILED) != 0) {
425                    // XXX
426                }
427            }
428        }
429        */
430        broadcastDataConnectionFailed(reason, apnType);
431    }
432
433    public void notifyCellLocation(Bundle cellLocation) {
434        if (!checkNotifyPermission("notifyCellLocation()")) {
435            return;
436        }
437        synchronized (mRecords) {
438            mCellLocation = cellLocation;
439            for (Record r : mRecords) {
440                if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
441                    sendCellLocation(r, cellLocation);
442                }
443            }
444        }
445    }
446
447    /**
448     * Copy the service state object so they can't mess it up in the local calls
449     */
450    private void sendServiceState(Record r, ServiceState state) {
451        try {
452            r.callback.onServiceStateChanged(new ServiceState(state));
453        } catch (RemoteException ex) {
454            remove(r.binder);
455        }
456    }
457
458    private void sendCellLocation(Record r, Bundle cellLocation) {
459        try {
460            r.callback.onCellLocationChanged(new Bundle(cellLocation));
461        } catch (RemoteException ex) {
462            remove(r.binder);
463        }
464    }
465
466    private void sendSignalStrength(Record r, SignalStrength signalStrength) {
467        try {
468            r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
469        } catch (RemoteException ex) {
470            remove(r.binder);
471        }
472    }
473
474    @Override
475    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
476        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
477                != PackageManager.PERMISSION_GRANTED) {
478            pw.println("Permission Denial: can't dump telephony.registry from from pid="
479                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
480            return;
481        }
482        synchronized (mRecords) {
483            final int recordCount = mRecords.size();
484            pw.println("last known state:");
485            pw.println("  mCallState=" + mCallState);
486            pw.println("  mCallIncomingNumber=" + mCallIncomingNumber);
487            pw.println("  mServiceState=" + mServiceState);
488            pw.println("  mSignalStrength=" + mSignalStrength);
489            pw.println("  mMessageWaiting=" + mMessageWaiting);
490            pw.println("  mCallForwarding=" + mCallForwarding);
491            pw.println("  mDataActivity=" + mDataActivity);
492            pw.println("  mDataConnectionState=" + mDataConnectionState);
493            pw.println("  mDataConnectionPossible=" + mDataConnectionPossible);
494            pw.println("  mDataConnectionReason=" + mDataConnectionReason);
495            pw.println("  mDataConnectionApn=" + mDataConnectionApn);
496            pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
497            pw.println("  mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
498            pw.println("  mCellLocation=" + mCellLocation);
499            pw.println("registrations: count=" + recordCount);
500            for (Record r : mRecords) {
501                pw.println("  " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
502            }
503        }
504    }
505
506    //
507    // the legacy intent broadcasting
508    //
509
510    private void broadcastServiceStateChanged(ServiceState state) {
511        long ident = Binder.clearCallingIdentity();
512        try {
513            mBatteryStats.notePhoneState(state.getState());
514        } catch (RemoteException re) {
515            // Can't do much
516        } finally {
517            Binder.restoreCallingIdentity(ident);
518        }
519
520        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
521        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
522        Bundle data = new Bundle();
523        state.fillInNotifierBundle(data);
524        intent.putExtras(data);
525        mContext.sendStickyBroadcast(intent);
526    }
527
528    private void broadcastSignalStrengthChanged(SignalStrength signalStrength) {
529        long ident = Binder.clearCallingIdentity();
530        try {
531            mBatteryStats.notePhoneSignalStrength(signalStrength);
532        } catch (RemoteException e) {
533            /* The remote entity disappeared, we can safely ignore the exception. */
534        } finally {
535            Binder.restoreCallingIdentity(ident);
536        }
537
538        Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
539        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
540        Bundle data = new Bundle();
541        signalStrength.fillInNotifierBundle(data);
542        intent.putExtras(data);
543        mContext.sendStickyBroadcast(intent);
544    }
545
546    private void broadcastCallStateChanged(int state, String incomingNumber) {
547        long ident = Binder.clearCallingIdentity();
548        try {
549            if (state == TelephonyManager.CALL_STATE_IDLE) {
550                mBatteryStats.notePhoneOff();
551            } else {
552                mBatteryStats.notePhoneOn();
553            }
554        } catch (RemoteException e) {
555            /* The remote entity disappeared, we can safely ignore the exception. */
556        } finally {
557            Binder.restoreCallingIdentity(ident);
558        }
559
560        Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
561        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
562        intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString());
563        if (!TextUtils.isEmpty(incomingNumber)) {
564            intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
565        }
566        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
567    }
568
569    private void broadcastDataConnectionStateChanged(int state,
570            boolean isDataConnectivityPossible,
571            String reason, String apn, String apnType, LinkProperties linkProperties,
572            LinkCapabilities linkCapabilities) {
573        // Note: not reporting to the battery stats service here, because the
574        // status bar takes care of that after taking into account all of the
575        // required info.
576        Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
577        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
578        intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString());
579        if (!isDataConnectivityPossible) {
580            intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true);
581        }
582        if (reason != null) {
583            intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
584        }
585        if (linkProperties != null) {
586            intent.putExtra(Phone.DATA_LINK_PROPERTIES_KEY, linkProperties);
587            NetworkInterface iface = linkProperties.getInterface();
588            if (iface != null) {
589                intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface.getName());
590            }
591        }
592        if (linkCapabilities != null) {
593            intent.putExtra(Phone.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
594        }
595        intent.putExtra(Phone.DATA_APN_KEY, apn);
596        intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
597        mContext.sendStickyBroadcast(intent);
598    }
599
600    private void broadcastDataConnectionFailed(String reason, String apnType) {
601        Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
602        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
603        intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
604        intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
605        mContext.sendStickyBroadcast(intent);
606    }
607
608    private boolean checkNotifyPermission(String method) {
609        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
610                == PackageManager.PERMISSION_GRANTED) {
611            return true;
612        }
613        String msg = "Modify Phone State Permission Denial: " + method + " from pid="
614                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
615        Slog.w(TAG, msg);
616        return false;
617    }
618
619    private void checkListenerPermission(int events) {
620        if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
621            mContext.enforceCallingOrSelfPermission(
622                    android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
623
624        }
625
626        if ((events & PHONE_STATE_PERMISSION_MASK) != 0) {
627            mContext.enforceCallingOrSelfPermission(
628                    android.Manifest.permission.READ_PHONE_STATE, null);
629        }
630    }
631}
632