Merge "Marking call as DIALING from CallsManager instead of Call."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 79b0f49..22db037 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,7 +16,9 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        package="com.android.telecomm">
+        package="com.android.telecomm"
+        coreApp="true"
+        android:sharedUserId="android.uid.phone">
 
     <!-- Prevents the activity manager from delaying any activity-start
          requests by this package, including requests immediately after
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 078209e..b21be69 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);
@@ -163,6 +166,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);
+    }
+}