blob: a74915c063bc8bb9fed9c1e2d27c78ec44477302 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -070031import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700191 if (!checkPhoneStatePermission("notifyCallState()")) {
192 return;
193 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700213 if (!checkPhoneStatePermission("notifyServiceState()")) {
214 return;
215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700230 if (!checkPhoneStatePermission("notifySignalStrength()")) {
231 return;
232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700251 if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) {
252 return;
253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700271 if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) {
272 return;
273 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700291 if (!checkPhoneStatePermission("notifyDataActivity()")) {
292 return;
293 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700312 if (!checkPhoneStatePermission("notifyDataConnection()")) {
313 return;
314 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700338 if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) {
339 return;
340 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 /*
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) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700359 if (!checkPhoneStatePermission("notifyCellLocation()")) {
360 return;
361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 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 Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
444 intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu);
445 mContext.sendStickyBroadcast(intent);
446 }
447
448 private void broadcastCallStateChanged(int state, String incomingNumber) {
449 long ident = Binder.clearCallingIdentity();
450 try {
451 if (state == TelephonyManager.CALL_STATE_IDLE) {
452 mBatteryStats.notePhoneOff();
453 } else {
454 mBatteryStats.notePhoneOn();
455 }
456 } catch (RemoteException e) {
457 } finally {
458 Binder.restoreCallingIdentity(ident);
459 }
460
461 Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
462 intent.putExtra(Phone.STATE_KEY,
463 DefaultPhoneNotifier.convertCallState(state).toString());
464 if (!TextUtils.isEmpty(incomingNumber)) {
465 intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
466 }
467 mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
468 }
469
470 private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible,
471 String reason, String apn, String interfaceName) {
472 Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
473 intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString());
474 if (!isDataConnectivityPossible) {
475 intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, true);
476 }
477 if (reason != null) {
478 intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
479 }
480 intent.putExtra(Phone.DATA_APN_KEY, apn);
481 intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
482 mContext.sendStickyBroadcast(intent);
483 }
484
485 private void broadcastDataConnectionFailed(String reason) {
486 Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
487 intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
488 mContext.sendStickyBroadcast(intent);
489 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700490
491 private boolean checkPhoneStatePermission(String method) {
492 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
493 == PackageManager.PERMISSION_GRANTED) {
494 return true;
495 }
496 String msg = "Modify Phone State Permission Denial: " + method + " from pid="
497 + Binder.getCallingPid()
498 + ", uid=" + Binder.getCallingUid();
499 Log.w(TAG, msg);
500 return false;
501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502}