Adding implementation of TelecommService
First use of this service is to allow silencing of the ringer
(called from TelephonyManager)
Also updated Telecomm to run under the android.uid.phone userId.
This ID has special permission to add services and send protected
broadcast intents. We'll piggy back until telecomm moves into the
system server process.
Bug: 15125169
Change-Id: I9180797451dcb2e9029b20bed47f5d5cb8cddb9f
diff --git a/src/com/android/telecomm/AsyncRingtonePlayer.java b/src/com/android/telecomm/AsyncRingtonePlayer.java
index ae78c5b..1c5cafe 100644
--- a/src/com/android/telecomm/AsyncRingtonePlayer.java
+++ b/src/com/android/telecomm/AsyncRingtonePlayer.java
@@ -16,6 +16,7 @@
package com.android.telecomm;
+import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.os.Handler;
@@ -149,7 +150,9 @@
private Ringtone getCurrentRingtone() {
// TODO: Needs support for custom ringtones.
- return RingtoneManager.getRingtone(
+ Ringtone ringtone = RingtoneManager.getRingtone(
TelecommApp.getInstance(), Settings.System.DEFAULT_RINGTONE_URI);
+ ringtone.setStreamType(AudioManager.STREAM_RING);
+ return ringtone;
}
}
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index a4b2086..48aa525 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -85,6 +85,8 @@
private final CallAudioManager mCallAudioManager;
+ private final Ringer mRinger;
+
private final Set<CallsManagerListener> mListeners = Sets.newHashSet();
/** Singleton accessor. */
@@ -99,12 +101,13 @@
TelecommApp app = TelecommApp.getInstance();
mCallAudioManager = new CallAudioManager();
-
InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager);
+ mRinger = new Ringer(mCallAudioManager, this, playerFactory, app);
+
mListeners.add(new CallLogManager(app));
mListeners.add(new PhoneStateBroadcaster());
mListeners.add(new InCallController());
- mListeners.add(new Ringer(mCallAudioManager, this, playerFactory, app));
+ mListeners.add(mRinger);
mListeners.add(new RingbackPlayer(this, playerFactory));
mListeners.add(new InCallToneMonitor(playerFactory, this));
mListeners.add(mCallAudioManager);
@@ -158,6 +161,10 @@
return mForegroundCall;
}
+ Ringer getRinger() {
+ return mRinger;
+ }
+
boolean hasEmergencyCall() {
for (Call call : mCalls) {
if (call.isEmergencyCall()) {
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
index 367a1de..3272103 100644
--- a/src/com/android/telecomm/Ringer.java
+++ b/src/com/android/telecomm/Ringer.java
@@ -46,7 +46,7 @@
* Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
* calls and explicit ordering is useful for maintaining the proper state of the ringer.
*/
- private final List<Call> mUnansweredCalls = Lists.newLinkedList();
+ private final List<Call> mRingingCalls = Lists.newLinkedList();
private final CallAudioManager mCallAudioManager;
private final CallsManager mCallsManager;
@@ -80,10 +80,10 @@
@Override
public void onCallAdded(Call call) {
if (call.isIncoming() && call.getState() == CallState.RINGING) {
- if (mUnansweredCalls.contains(call)) {
+ if (mRingingCalls.contains(call)) {
Log.wtf(this, "New ringing call is already in list of unanswered calls");
}
- mUnansweredCalls.add(call);
+ mRingingCalls.add(call);
updateRinging();
}
}
@@ -113,12 +113,21 @@
@Override
public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
- if (mUnansweredCalls.contains(oldForegroundCall) ||
- mUnansweredCalls.contains(newForegroundCall)) {
+ if (mRingingCalls.contains(oldForegroundCall) ||
+ mRingingCalls.contains(newForegroundCall)) {
updateRinging();
}
}
+ /**
+ * Silences the ringer for any actively ringing calls.
+ */
+ void silence() {
+ // Remove all calls from the "ringing" set and then update the ringer.
+ mRingingCalls.clear();
+ updateRinging();
+ }
+
private void onRespondedToIncomingCall(Call call) {
// Only stop the ringer if this call is the top-most incoming call.
if (getTopMostUnansweredCall() == call) {
@@ -126,26 +135,26 @@
stopCallWaiting();
}
- // We do not remove the call from mUnansweredCalls until the call state changes from RINGING
+ // We do not remove the call from mRingingCalls until the call state changes from RINGING
// or the call is removed. see onCallStateChanged or onCallRemoved.
}
private Call getTopMostUnansweredCall() {
- return mUnansweredCalls.isEmpty() ? null : mUnansweredCalls.get(0);
+ return mRingingCalls.isEmpty() ? null : mRingingCalls.get(0);
}
/**
* Removes the specified call from the list of unanswered incoming calls and updates the ringer
- * based on the new state of {@link #mUnansweredCalls}. Safe to call with a call that is not
+ * based on the new state of {@link #mRingingCalls}. Safe to call with a call that is not
* present in the list of incoming calls.
*/
private void removeFromUnansweredCall(Call call) {
- mUnansweredCalls.remove(call);
+ mRingingCalls.remove(call);
updateRinging();
}
private void updateRinging() {
- if (mUnansweredCalls.isEmpty()) {
+ if (mRingingCalls.isEmpty()) {
stopRinging();
stopCallWaiting();
} else {
@@ -157,7 +166,7 @@
Call foregroundCall = mCallsManager.getForegroundCall();
Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
- if (mUnansweredCalls.contains(foregroundCall)) {
+ if (mRingingCalls.contains(foregroundCall)) {
// The foreground call is one of incoming calls so play the ringer out loud.
stopCallWaiting();
diff --git a/src/com/android/telecomm/TelecommApp.java b/src/com/android/telecomm/TelecommApp.java
index 49554da..48f6ba5 100644
--- a/src/com/android/telecomm/TelecommApp.java
+++ b/src/com/android/telecomm/TelecommApp.java
@@ -36,7 +36,9 @@
@Override public void onCreate() {
super.onCreate();
sInstance = this;
+
mMissedCallNotifier = new MissedCallNotifier(this);
+ TelecommServiceImpl.init();
}
public static TelecommApp getInstance() {
diff --git a/src/com/android/telecomm/TelecommServiceImpl.java b/src/com/android/telecomm/TelecommServiceImpl.java
new file mode 100644
index 0000000..beb046b
--- /dev/null
+++ b/src/com/android/telecomm/TelecommServiceImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 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.telecomm;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+
+import com.android.internal.telecomm.ITelecommService;
+
+/**
+ * Implementation of the ITelecomm interface.
+ */
+public class TelecommServiceImpl extends ITelecommService.Stub {
+ private static final String TAG = TelecommServiceImpl.class.getSimpleName();
+
+ private static final String SERVICE_NAME = "telecomm";
+
+ private static final int MSG_SILENCE_RINGER = 1;
+
+ /** The singleton instance. */
+ private static TelecommServiceImpl sInstance;
+
+ /**
+ * A handler that processes messages on the main thread in the phone process. Since many
+ * of the Phone calls are not thread safe this is needed to shuttle the requests from the
+ * inbound binder threads to the main thread in the phone process.
+ */
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SILENCE_RINGER:
+ silenceRingerInternal();
+ break;
+ }
+ }
+ };
+
+ /**
+ * Initialize the singleton TelecommServiceImpl instance.
+ * This is only done once, at startup, from TelecommApp.onCreate().
+ */
+ static TelecommServiceImpl init() {
+ synchronized (TelecommServiceImpl.class) {
+ if (sInstance == null) {
+ sInstance = new TelecommServiceImpl();
+ } else {
+ Log.wtf(TAG, "init() called multiple times! sInstance %s", sInstance);
+ }
+ return sInstance;
+ }
+ }
+
+ /** Private constructor; @see init() */
+ private TelecommServiceImpl() {
+ publish();
+ }
+
+ private void publish() {
+ Log.d(this, "publish: %s", this);
+ ServiceManager.addService(SERVICE_NAME, this);
+ }
+
+ //
+ // Implementation of the ITelephony interface.
+ //
+
+ @Override
+ public void silenceRinger() {
+ Log.d(this, "silenceRinger");
+ // TODO: find a more appropriate permission to check here.
+ enforceModifyPermission();
+ mHandler.sendEmptyMessage(MSG_SILENCE_RINGER);
+ }
+
+ /**
+ * Internal implemenation of silenceRinger().
+ * This should only be called from the main thread of the Phone app.
+ * @see #silenceRinger
+ */
+ private void silenceRingerInternal() {
+ CallsManager.getInstance().getRinger().silence();
+ }
+
+ /**
+ * Make sure the caller has the MODIFY_PHONE_STATE permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceModifyPermission() {
+ TelecommApp.getInstance().enforceCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE, null);
+ }
+}