Mark self-managed calls as "read" when logged.

When self-maanged calls are logged to the call log, ensure that the is_read
flag is set to 1.  This is important for missed calls.  Missed calls which
are unread trigger a missed-call notification.  Realistically we expect
the app to handle the missed called notification on its own, so marking
them as read makes sense.

Also updated the example self managed calling test app to support missed
calls.  Yay.

Test: Added unit tests for this scenario, used manual test harness.
Fixes: 67637603
Merged-In: I0ad0917faed0e9c9902e33489f8715a73b350050
Change-Id: I0ad0917faed0e9c9902e33489f8715a73b350050
(cherry picked from commit 137df1f31a3d1e8b1ab35db9e6bd15ab48f10a8f)
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index ce062c0..496b6f4 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -69,12 +69,13 @@
          * @param creationDate Time when the call was created (milliseconds since epoch).
          * @param durationInMillis Duration of the call (milliseconds).
          * @param dataUsage Data usage in bytes, or null if not applicable.
+         * @param isRead Indicates if the entry has been read or not.
          * @param logCallCompletedListener optional callback called after the call is logged.
          */
         public AddCallArgs(Context context, CallerInfo callerInfo, String number,
                 String postDialDigits, String viaNumber, int presentation, int callType,
                 int features, PhoneAccountHandle accountHandle, long creationDate,
-                long durationInMillis, Long dataUsage, UserHandle initiatingUser,
+                long durationInMillis, Long dataUsage, UserHandle initiatingUser, boolean isRead,
                 @Nullable LogCallCompletedListener logCallCompletedListener) {
             this.context = context;
             this.callerInfo = callerInfo;
@@ -89,6 +90,7 @@
             this.durationInSec = (int)(durationInMillis / 1000);
             this.dataUsage = dataUsage;
             this.initiatingUser = initiatingUser;
+            this.isRead = isRead;
             this.logCallCompletedListener = logCallCompletedListener;
         }
         // Since the members are accessed directly, we don't use the
@@ -106,6 +108,7 @@
         public final int durationInSec;
         public final Long dataUsage;
         public final UserHandle initiatingUser;
+        public final boolean isRead;
 
         @Nullable
         public final LogCallCompletedListener logCallCompletedListener;
@@ -235,7 +238,7 @@
         logCall(call.getCallerInfo(), logNumber, call.getPostDialDigits(), formattedViaNumber,
                 call.getHandlePresentation(), callLogType, callFeatures, accountHandle,
                 creationTime, age, callDataUsage, call.isEmergencyCall(), call.getInitiatingUser(),
-                logCallCompletedListener);
+                call.isSelfManaged(), logCallCompletedListener);
     }
 
     /**
@@ -253,6 +256,8 @@
      * @param dataUsage The data usage for the call, null if not applicable.
      * @param isEmergency {@code true} if this is an emergency call, {@code false} otherwise.
      * @param logCallCompletedListener optional callback called after the call is logged.
+     * @param initiatingUser The user the call was initiated under.
+     * @param isSelfManaged {@code true} if this is a self-managed call, {@code false} otherwise.
      */
     private void logCall(
             CallerInfo callerInfo,
@@ -268,6 +273,7 @@
             Long dataUsage,
             boolean isEmergency,
             UserHandle initiatingUser,
+            boolean isSelfManaged,
             @Nullable LogCallCompletedListener logCallCompletedListener) {
 
         // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
@@ -289,12 +295,18 @@
         sendAddCallBroadcast(callType, duration);
 
         if (isOkToLogThisCall) {
-            Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
+            Log.d(TAG, "Logging Call log entry: " + callerInfo + ", "
                     + Log.pii(number) + "," + presentation + ", " + callType
                     + ", " + start + ", " + duration);
+            boolean isRead = false;
+            if (isSelfManaged) {
+                // Mark self-managed calls are read since they're being handled by their own app.
+                // Their inclusion in the call log is informational only.
+                isRead = true;
+            }
             AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, postDialDigits,
                     viaNumber, presentation, callType, features, accountHandle, start, duration,
-                    dataUsage, initiatingUser, logCallCompletedListener);
+                    dataUsage, initiatingUser, isRead, logCallCompletedListener);
             logCallAsync(args);
         } else {
           Log.d(TAG, "Not adding emergency call to call log.");
@@ -440,7 +452,7 @@
             return Calls.addCall(c.callerInfo, c.context, c.number, c.postDialDigits, c.viaNumber,
                     c.presentation, c.callType, c.features, c.accountHandle, c.timestamp,
                     c.durationInSec, c.dataUsage, userToBeInserted == null,
-                    userToBeInserted);
+                    userToBeInserted, c.isRead);
         }
 
 
diff --git a/testapps/res/layout/self_managed_call_list_item.xml b/testapps/res/layout/self_managed_call_list_item.xml
index f3be4ad..7e149a8 100644
--- a/testapps/res/layout/self_managed_call_list_item.xml
+++ b/testapps/res/layout/self_managed_call_list_item.xml
@@ -50,6 +50,11 @@
         <Button
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:text="Missed"
+            android:id="@+id/missedButton" />
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:text="Disconnect"
             android:id="@+id/disconnectButton" />
     </LinearLayout>
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
index b46d5e1..71e8922 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
@@ -60,8 +60,18 @@
     };
 
     /**
-     * Listener used to handle tap of the "held" button for a connection.
+     * Listener used to handle tap of the "missed" button for a connection.
      */
+    private View.OnClickListener mMissedListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            View parent = (View) v.getParent().getParent();
+            SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+            connection.setConnectionDisconnected(DisconnectCause.MISSED);
+            SelfManagedCallList.getInstance().removeConnection(connection);
+        }
+    };
+
     private View.OnClickListener mHeldListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -165,7 +175,7 @@
         }
         setInfoForRow(result, phoneAccountHandle.getId(), connection.getAddress().toString(),
                 android.telecom.Connection.stateToString(connection.getState()), audioRoute,
-                callType);
+                callType, connection.getState() == android.telecom.Connection.STATE_RINGING);
         result.setTag(connection);
         return result;
     }
@@ -177,7 +187,8 @@
     }
 
     private void setInfoForRow(View view, String accountName, String number,
-                               String status, String audioRoute, String callType) {
+                               String status, String audioRoute, String callType,
+            boolean isRinging) {
 
         TextView numberTextView = (TextView) view.findViewById(R.id.phoneNumber);
         TextView statusTextView = (TextView) view.findViewById(R.id.callState);
@@ -191,6 +202,11 @@
         speakerButton.setOnClickListener(mSpeakerListener);
         View earpieceButton = view.findViewById(R.id.earpieceButton);
         earpieceButton.setOnClickListener(mEarpieceListener);
+        View missedButton = view.findViewById(R.id.missedButton);
+        missedButton.setOnClickListener(mMissedListener);
+        missedButton.setVisibility(isRinging ? View.VISIBLE : View.GONE);
+        setHeldButton.setVisibility(!isRinging ? View.VISIBLE : View.GONE);
+        disconnectButton.setVisibility(!isRinging ? View.VISIBLE : View.GONE);
         numberTextView.setText(accountName + " - " + number + " (" + audioRoute + ")");
         statusTextView.setText(callType + " - Status: " + status);
     }
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
index 82967c4..a84dd90 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
@@ -183,6 +183,9 @@
     }
 
     public void setConnectionDisconnected(int cause) {
+        NotificationManager notificationManager = mContext.getSystemService(
+                NotificationManager.class);
+        notificationManager.cancel(CALL_NOTIFICATION, mCallId);
         mMediaPlayer.stop();
         setDisconnected(new DisconnectCause(cause));
         destroy();
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
index bb34530..12d1552 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
@@ -79,6 +79,7 @@
         connection.setExtras(request.getExtras());
         if (isIncoming) {
             connection.setIsIncomingCallUiShowing(request.shouldShowIncomingCallUi());
+            connection.setRinging();
         }
         Bundle requestExtras = request.getExtras();
         if (requestExtras != null) {
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index 52f1b0f..5840b07 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -46,6 +46,7 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallLogManager;
 import com.android.server.telecom.CallState;
+import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelephonyUtil;
@@ -83,6 +84,7 @@
     private PhoneAccountHandle mDefaultAccountHandle;
     private PhoneAccountHandle mOtherUserAccountHandle;
     private PhoneAccountHandle mManagedProfileAccountHandle;
+    private PhoneAccountHandle mSelfManagedAccountHandle;
 
     private static final Uri TEL_PHONEHANDLE = Uri.parse("tel:5555551234");
 
@@ -95,6 +97,7 @@
     private static final String POST_DIAL_STRING = ";12345";
     private static final String VIA_NUMBER_STRING = "5555555678";
     private static final String TEST_PHONE_ACCOUNT_ID= "testPhoneAccountId";
+    private static final String TEST_SELF_MGD_PHONE_ACCOUNT_ID= "testPhoneAccountId";
 
     private static final int TEST_TIMEOUT_MILLIS = 200;
     private static final int CURRENT_USER_ID = 0;
@@ -136,6 +139,12 @@
                 UserHandle.of(MANAGED_USER_ID)
         );
 
+        mSelfManagedAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerSelfMgdTest"),
+                TEST_SELF_MGD_PHONE_ACCOUNT_ID,
+                UserHandle.of(CURRENT_USER_ID)
+        );
+
         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         UserInfo userInfo = new UserInfo(CURRENT_USER_ID, "test", 0);
         UserInfo otherUserInfo = new UserInfo(OTHER_USER_ID, "test2", 0);
@@ -654,6 +663,37 @@
         assertNull(insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
     }
 
+    /**
+     * Ensures missed self-managed calls are marked as read..
+     */
+    @MediumTest
+    @Test
+    public void testLogMissedSelfManaged() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle,
+                        PhoneAccount.CAPABILITY_SELF_MANAGED));
+        Call fakeMissedCall = makeFakeCall(
+                DisconnectCause.MISSED, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mSelfManagedAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                VIA_NUMBER_STRING, // viaNumber
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        when(fakeMissedCall.isSelfManaged()).thenReturn(true);
+        when(fakeMissedCall.isLoggedSelfManaged()).thenReturn(true);
+        when(fakeMissedCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_NONE);
+        mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(1, insertedValues.getAsInteger(Calls.IS_READ).intValue());
+    }
+
     @SmallTest
     @Test
     public void testCountryIso_setCache() {