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